Continuando a sequência de artigos sobre a placa Curiosity da Microchip, neste artigo vamos explorar os pinos de I/Os do PIC16F1619 presentes nela.
Ao final deste artigo o leitor estará apto a utilizar os pinos de I/O para leitura e escrita de sinais digitais.
Pinos de I/O na Curiosity
Os pinos de I/O são os meios pelos quais os microcontroladores se comunicam com o mundo externo. Geralmente podem ser configurados como entradas ou saídas, dessa forma é possível fazer a leitura de teclas (pino como entrada), acionar LEDs (pino como saída), ligar/desligar Reles (pino como saída), por exemplo.
Os pinos presentes no microcontrolador estão agrupados em ports, geralmente com 8 ou menos pinos, conforme a configuração do microcontrolador. Os ports são identificados como PORTA, PORTB, PORTC, etc. Cada port possui um grupo de registradores para configuração, leitura e escrita, que estudaremos mais adiante.
O PIC16F1619 possui 3 ports: PORTA, PORTB e PORTC. Os pinos geralmente apresentam funções alternativas e são multiplexadas para que se possa realizar diversas funções em um mesmo pino, conforme a necessidade da aplicação. Porém, essas funções são habilitadas com o periférico que utilizará tal pino.
Os pinos são organizados no microcontrolador, conforme seu port e seguindo uma sequência. Por exemplo o pino 0 do PORTA é chamado de RA0, o pino 5 do PORTB como RB5, e assim por diante. A figura 1 exibe os pinos do PIC16F1619:
Cada port possui basicamente 3 registradores para controle dos pinos, que são:
- TRISx – tem a função de definir a direção do pino, ou seja, se funcionará como uma entrada ou saída;
- PORTx – é onde será feita a leitura do estado lógico presente no pino;
- LATx – retêm a informação no lach (flip-flop tipo D) no port.
Além desses registradores básicos, alguns ports possuem outros registradores de configuração, por exemplo:
- ANSELx – Configura se o pino será digital ou analógico;
- WPUx – Configura resistores de pull-up.
A figura 2 exibe o diagrama de funcionamento de um pino de I/O do PIC16F1619:
Configurando um pino de I/O como Entrada ou Saída
Para configurar um pino de I/O como entrada ou saída deve-se utilizar o registrador TRISx. Para configurar um pino como entrada deve-se escrever o valor lógico 1 no bit correspondente. Já para configurar o pino como saída, deve-se escrever o valor lógico 0 no bit correspondente ao pino. A figura 3 exibe o registrador TRISA, responsável pela configuração da direção dos pinos do PORTA:
Por exemplo, se quisermos configurar o pino RA0 como entrada, devemos escrever o valor 1 no bit 0 do TRISA. Se o projeto exigir que o pino seja configurado como saída digital, deve-se escrever o valor 0 nesse pino.
A configuração do PORTB e PORTC segue a mesma ideia. Os registradores TRISB e TRISC são exibidos na figura 4:
Além disso, alguns pinos podem ser configurados como entradas analógicas. No nosso caso estamos trabalhando apenas com pinos de I/O digitais, dessa forma devemos configurar o registrador ANSELX para o correto funcionamento do pino. A figura 5 exibe o registrador ANSELA:
Para configurar o pino para funcionar como I/O digital deve-se escrever o valor zero no pino correspondente. Caso o pino for uma entrada analógica, deve-se escrever o valor 1. Para o PORTB e PORTC segue o mesmo padrão de configuração, conforme os registradores ANSELB e ANSELC exibidos na figura 6:
Lendo o estado de um pino de I/O
Quando um pino é configurado com entrada, desejamos ler o valor lógico presente nesse pino, ou seja, será um nível lógico 0 ou nível lógico 1, conforme a tensão presente no pino.
Para leitura utiliza-se o registrador PORTx. O valor presente nesse registrador conterá o nível lógico presente no pino. A figura 7 exibe o registrador PORTA.
Para leitura dos pinos do PORTB e PORTC segue o mesmo padrão. A figura 8 exibe esses registradores:
Escrevendo em um pino de I/O
Quando um pino é configurado como saída, podemos escrever o valor lógico 0 ou o valor lógico 1, conforme a necessidade. Para escrita é utilizado o registrador LATx. Escrevendo o valor 1 coloca-se nível lógico alto no pino, ou seja, tensão máxima. Já quando escrito o valor 0 coloca-se nível lógico baixo no pino, ou seja, tensão vai pra zero.
A figura 9 exibe os registradores LATA, LATB e LATC:
Exemplo de leitura e escrita nos pinos
Para testar a teoria apresentada vamos criar uma aplicação para leitura de tecla e escrita em LEDs. Para isso vamos usar a chave táctil (S1) presente na Curiosity, assim como os LEDs (D4, D5, D6 e D7). Cada vez que a tecla for pressionada será ligado um LED e apagados os outros, dando efeito de deslocamento.
Primeiro precisamos identificar no esquema elétrico em quais pinos estão ligados os componentes. A figura 10 exibe parte do esquemático da Curiosity:
Através do esquemático, verificamos que a tecla S1 está ligada ao pino RC4, o LED D4 ao pino RA5, o LED D5 ao pino RA1, o LED D6 ao pino RA2 e o LED D7 ao pino RC5. Dessa forma devemos configurar os pinos dos LEDs como saída e o pino da tecla como entrada.
Para exemplificar vamos fazer o programa sem o uso do MPLAB Code Configurator a principio e, posteriormente, o mesmo programa usando o MPLAB Code Configurator.
Exemplo sem o uso do MCC
Para esse exemplo deve-se seguir os passos para criação de um projeto, apresentados no artigo: Crie projetos com a placa Curiosity: Primeiros passos com MPLAB X e compilador XC8
Após a inicialização e configuração inicial vamos inserir o seguinte código no arquivo main.c:
/*
* File: main.c
* Author: FábioSouza
*
* Created on 6 de Janeiro de 2016, 01:03
*/
// PIC16F1619 Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON // Internal/External Switch Over (Internal External Switch Over mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PPS1WAY = ON // Peripheral Pin Select one-way control (The PPSLOCK bit cannot be cleared once it is set by software)
#pragma config ZCD = OFF // Zero Cross Detect Disable Bit (ZCD disable. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON)
#pragma config PLLEN = ON // PLL Enable Bit (4x PLL is always enabled)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = ON // Low-Power Brown Out Reset (Low-Power BOR is enabled)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
// CONFIG3
#pragma config WDTCPS = WDTCPSE // WDT Period Select (1:524299 (16 s period))
#pragma config WDTE = SWDTEN // Watchdog Timer Enable (WDT controlled by the SWDTEN bit in the WDTCON register)
#pragma config WDTCWS = WDTCWS100// WDT Window Select (100 percent window open time (Legacy WDT) )
#pragma config WDTCCS = MFINTOSC// WDT Input Clock Selector (31.25 kHz HFINTOSC (MFINTOSC))
#define _XTAL_FREQ 500000 //define para utilizar funções de tempo - Osc em 500 KHz
//constantes
#define PRESS 1 //sinaliza botão pressionado
#define NOPRESS 0 //sinaliza botão solto
//variáveis
unsigned char posicao = 0; //variável auxiliar para posição dos leds
bit ST_BT; //flag para indicar o status do botão
void main(void) {
//configura PORTA
LATA = 0; //desliga todas as saídas
TRISA = 0B11011001; //RA5 e RA2 como saídas, demais pinos como entrada
ANSELA = 0; //pinos como digitais
//configura PORTB
LATB = 0; //desliga todos as saídas
TRISB = 0XFF; //todos os pinos como entradas
ANSELB = 0; //pinos como digitais
//configura PORTC
LATC = 0; //desliga todos as saídas
TRISC = 0B11011111; //todos os pinos como entradas
ANSELB = 0; //pinos como digitais
while(1){
if(RC4 == 0){ //se botão pressionado
if(ST_BT == NOPRESS){ //se não estava pressionado
ST_BT = PRESS; //sinaliza que o botão foi pressionado
posicao++; //incrementa indexador dos LEDs
if(posicao>=4)posicao = 0; //se chegou no máximo reinicia
}
}
else{
ST_BT = NOPRESS;
}
//atualiza leds
switch(posicao){
case 0:
RA5 = 1; //D4 = LIGADO
RA1 = 0; //D5 = DESLIGADO
RA2 = 0; //D6 = DESLIGADO
RC5 = 0; //D7 = DESLIGADO
break;
case 1:
RA5 = 0; //D4 = DESLIGADO
RA1 = 1; //D5 = LIGADO
RA2 = 0; //D6 = DESLIGADO
RC5 = 0; //D7 = DESLIGADO
break;
case 2:
RA5 = 0; //D4 = DESLIGADO
RA1 = 0; //D5 = DESLIGADO
RA2 = 1; //D6 = LIGADO
RC5 = 0; //D7 = DESLIGADO
break;
case 3:
RA5 = 0; //D4 = DESLIGADO
RA1 = 0; //D5 = DESLIGADO
RA2 = 0; //D6 = DESLIGADO
RC5 = 1; //D7 = LIGADO
break;
}
}
}
Compilando e executando o código acima na placa, a cada pressionamento da tecla será acendido um LED. Note como foi feita a configuração dos registradores e como foi elaborado o loop principal para leitura e escrita nos pinos.
Para deixar o código mais fácil de se entender, podemos criar defines que ajudam a identificar os LEDs e botão. O código a seguir apresenta essas modificações:
/*
* File: main.c
* Author: FábioSouza
*
* Created on 6 de Janeiro de 2016, 01:03
*/
// PIC16F1619 Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON // Internal/External Switch Over (Internal External Switch Over mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PPS1WAY = ON // Peripheral Pin Select one-way control (The PPSLOCK bit cannot be cleared once it is set by software)
#pragma config ZCD = OFF // Zero Cross Detect Disable Bit (ZCD disable. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON)
#pragma config PLLEN = ON // PLL Enable Bit (4x PLL is always enabled)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = ON // Low-Power Brown Out Reset (Low-Power BOR is enabled)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
// CONFIG3
#pragma config WDTCPS = WDTCPSE // WDT Period Select (1:524299 (16 s period))
#pragma config WDTE = SWDTEN // Watchdog Timer Enable (WDT controlled by the SWDTEN bit in the WDTCON register)
#pragma config WDTCWS = WDTCWS100// WDT Window Select (100 percent window open time (Legacy WDT) )
#pragma config WDTCCS = MFINTOSC// WDT Input Clock Selector (31.25 kHz HFINTOSC (MFINTOSC))
#define _XTAL_FREQ 500000 //define para utilizar funções de tempo - Osc em 500 KHz
//constantes
#define PRESS 1 //sinaliza botão pressionado
#define NOPRESS 0 //sinaliza botão solto
#define HIGH 1
#define LOW 0
#define POSICAO_FINAL 4
#define POSICAO_INICIAL 0
#define D4 RA5
#define D5 RA1
#define D6 RA2
#define D7 RC5
#define TECLA RC4
//variáveis
unsigned char posicao = 0; //variável auxiliar para posição dos leds
bit ST_BT; //flag para indicar o status do botão
void main(void) {
//configura PORTA
LATA = 0; //desliga todas as saídas
TRISA = 0B11011001; //RA5 e RA2 como saídas, demais pinos como entrada
ANSELA = 0; //pinos como digitais
//configura PORTB
LATB = 0; //desliga todos as saídas
TRISB = 0XFF; //todos os pinos como entradas
ANSELB = 0; //pinos como digitais
//configura PORTC
LATC = 0; //desliga todos as saídas
TRISC = 0B11011111; //todos os pinos como entradas
ANSELB = 0; //pinos como digitais
while(1){
if(TECLA == LOW){ //se botão pressionado
if(ST_BT == NOPRESS){ //se não estava pressionado
ST_BT = PRESS; //sinaliza que o botão foi pressionado
posicao++; //incrementa indexador dos LEDs
if(posicao>=POSICAO_FINAL)posicao = POSICAO_INICIAL; //se chegou no máximo reinicia
}
}
else{
ST_BT = NOPRESS;
}
//atualiza leds
switch(posicao){
case 0:
D4 = HIGH; //D4 = LIGADO
D5 = LOW; //D5 = DESLIGADO
D6 = LOW; //D6 = DESLIGADO
D7 = LOW; //D7 = DESLIGADO
break;
case 1:
D4 = LOW; //D4 = DESLIGADO
D5 = HIGH; //D5 = LIGADO
D6 = LOW; //D6 = DESLIGADO
D7 = LOW; //D7 = DESLIGADO
break;
case 2:
D4 = LOW; //D4 = DESLIGADO
D5 = LOW; //D5 = DESLIGADO
D6 = HIGH; //D6 = LIGADO
D7 = LOW; //D7 = DESLIGADO
break;
case 3:
D4 = LOW; //D4 = DESLIGADO
D5 = LOW; //D5 = DESLIGADO
D6 = LOW; //D6 = DESLIGADO
D7 = HIGH; //D7 = LIGADO
break;
}
}
}
Exemplo com o uso do MCC
Para exemplificar, vamos fazer a mesma aplicação utilizando o MPLAB Code Configurator. Para isso deve-se seguir os passos apresentados no artigo Crie projetos com a placa Curiosity: Iniciando com o MPLAB Code Configurator.
A configuração dos pinos deve ficar conforme apresentado na figura 11:
Foram gerados os aquivos pelo MCC, porém vamos verificar apenas como ficou a função para configuração dos pinos de I/O, no arquivo pin_manager.c:
void PIN_MANAGER_Initialize(void) {
LATA = 0x00;
TRISA = 0x19;
ANSELA = 0x11;
LATB = 0x00;
TRISB = 0xF0;
ANSELB = 0x30;
WPUB = 0x00;
LATC = 0x00;
TRISC = 0xDF;
ANSELC = 0xCF;
WPUC = 0x00;
OPTION_REGbits.nWPUEN = 0x01;
}
Note que foram configurados os registradores associados a cada pino, conforme as configurações feitas no MCC. A aplicação no loop principal ficará igual ao projeto feito sem o MCC, porém nesse caso foram utilizadas as macros de manipulação dos pinos:
#include "mcc_generated_files/mcc.h"
#define NOPRESS 0
#define PRESS 1
#define POSICAO_FINAL 4
#define POSICAO_INICIAL 0
//variáveis
unsigned char posicao = 0; //variável auxiliar para posição dos leds
bit ST_BT; //flag para indicar o status do botão
/*
Main application
*/
void main(void) {
// initialize the device
SYSTEM_Initialize();
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
// Use the following macros to:
// Enable the Global Interrupts
//INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
// Disable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptDisable();
while (1) {
if(TECLA_GetValue() == LOW){ //se botão pressionado
if(ST_BT == NOPRESS){ //se não estava pressionado
ST_BT = PRESS; //sinaliza que o botão foi pressionado
posicao++; //incrementa indexador dos LEDs
if(posicao>=POSICAO_FINAL)posicao = POSICAO_INICIAL; //se chegou no máximo reinicia
}
}
else{
ST_BT = NOPRESS;
}
//atualiza leds
switch(posicao){
case 0:
D4_SetHigh(); //D4 = LIGADO
D5_SetLow(); //D5 = DESLIGADO
D6_SetLow(); //D6 = DESLIGADO
D7_SetLow(); //D7 = DESLIGADO
break;
case 1:
D4_SetLow(); //D4 = DESLIGADO
D5_SetHigh(); //D5 = LIGADO
D6_SetLow(); //D6 = DESLIGADO
D7_SetLow(); //D7 = DESLIGADO
break;
case 2:
D4_SetLow(); //D4 = DESLIGADO
D5_SetLow(); //D5 = DESLIGADO
D6_SetHigh(); //D6 = LIGADO
D7_SetLow(); //D7 = DESLIGADO
break;
case 3:
D4_SetLow(); //D4 = DESLIGADO
D5_SetLow(); //D5 = DESLIGADO
D6_SetLow(); //D6 = DESLIGADO
D7_SetHigh(); //D7 = LIGADO
break;
}
}
}
O resultado é apresentado na Figura 12, onde a cada pressionamento da tecla é deslocado um LED:

O artigo exibiu os detalhes para configuração e manipulação dos pinos utilizando o compilador XC8. Foram apresentados todos os registradores assim como um exemplo de aplicação.
Você pode acessar o exemplo completo no MPLAB XPress.
No próximo artigo vamos aprender a trabalhar com os Timers.
Caso tenha alguma dúvida ou sugestão, deixe seu comentário abaixo.





