ÍNDICE DE CONTEÚDO
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
/* * 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
/* * 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#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.