Estou tentando desenvolver uma programação no AVR Studio com o ATmega168 e gostaria de atingir o seguinte objetivo:
Ler vários canais ADC e quando dado um click no botão PB0 mostrar resultado no LCD, quando dado um segundo click no mesmo botão enviar essas informações via serial...
No entanto não estou conseguindo nem um nem outro, caso alguém possa ajudar o codigo segue abaixo.
Obs:. O controle de interrupção deve controladar o estado da entrada de um único botão usando um único contador de 8 bits para permitir que vários controles.
Características:
- 2 Segundos apertado;
- 5 Segundos apertado;
- rápida, 10 segundos tempo limite;
- Código: Selecionar todos
#include <avr/io.h>
#include <util/delay.h>
#include <compat/twi.h>
#include <math.h>
#include <compat/deprecated.h>
#include <avr/interrupt.h>
#include <stdlib.h> //Necessary for the function itoa
#include <avr/pgmspace.h>
#define F_CPU 11059200UL
#define BAUD_RATE 19200
#define tbi(port, bit) (port & (1 << bit))
#define button_press !tbi(PINB,PB0)
//Button Press State Variable Enumeration
#define IPRESS 0x01
#define RELEASE 0x02
#define IDLE 0x04
// Timeouts
#define TIME_OV 148 //10 sec timeout
#define TIME_2S 40 //2 second hold
#define TIME_5S 80 //5 second hold
volatile uint8_t button_state,hold5, hold2, press;
volatile uint16_t time; // extended time counter
//------------------------------------------------------------------------------------------------------------------
// Definitions LCD 16x2 (Hitachi HD44780U)
//------------------------------------------------------------------------------------------------------------------
#define LCD_HOME 0x02
#define LCD_NEXT_LINE 0xC0
#define LCD_CLEAR 0x01
#define LCD_1CYCLE 0
#define LCD_2CYCLE 1
char sdigit[3]={'0','0','\0'};
//------------------------------------------------------------------------------------------------------------------
// Handling communication with the LCD
//------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------
// Subroutine display single character on the LCD (Send Data)
//------------------------------------------------------------------------------------------------------------------
void LCD_putch(unsigned char data) //Function used to display single character on the LCD
{
//LCD Upper 4 bits data (DB7,DB6,DB5,DB4)
PORTD = (1<<PD3)|(1<<PD2)|(data & 0xF0); //RS = 1, E = 1
// E=0; write data
PORTD &= ~(1<<PD3);
_delay_us(1);
//LCD Lower 4 bits data (DB3,DB2,DB1,DB0)
PORTD = (1<<PD3)|(1<<PD2)|((data & 0x0F) << 4); //RS = 1, E = 1 (<< shifts 4 bits left)
//E=0; write data
PORTD &= ~(1<<PD3);
_delay_ms(5); //Wait for busy flag (BF)
}
//------------------------------------------------------------------------------------------------------------------
// Subroutine sending commands to the LCD (Send Instruction)
//------------------------------------------------------------------------------------------------------------------
void LCD_putcmd(unsigned char data,unsigned char cmdtype) //Function used to send LCD command
{
//LCD Upper 4 bits data (DB7,DB6,DB5,DB4)
PORTD = (1<<PD3)|(data & 0xF0); //RS = 0, E = 1
//E=0; write data
PORTD &= ~(1<<PD3);
_delay_us(1);
//cmdtype = 0; One cycle write, cmdtype = 1; Two cycle writes
if (cmdtype)
{
//LCD Lower 4 bits data (DB3,DB2,DB1,DB0)
PORTD = (1<<PD3)|((data & 0x0F) << 4); //RS = 0, E = 1 (<< shifts 4 bits left)
//E=0; write data
PORTD &= ~(1<<PD3);
}
_delay_ms(5); //Wait for busy flag (BF)
}
//------------------------------------------------------------------------------------------------------------------
// Subroutine initialized the LCD 16x2 mode 4bits
//------------------------------------------------------------------------------------------------------------------
void initlcd() //Function used to initialized the 16x2 LCD
{
//Power ON - Stabilization
//Wait for more than 15 ms after VCC rises to 4.5 V
_delay_ms(30);
//Send Command Instruction 0x30 (1 cycle)
LCD_putcmd(0x30,LCD_1CYCLE);
_delay_ms(8); //Wait for more than 4.1 ms
//Send Command Instruction 0x30 (1 cycle)
LCD_putcmd(0x30,LCD_1CYCLE);
_delay_us(200); //Wait for more than 100 us
//Send Command Instruction 0x30 (1 cycle)
LCD_putcmd(0x30,LCD_1CYCLE);
//Function set: Set interface to be 4 bits data long (only 1 cycle write)
LCD_putcmd(0x20,LCD_1CYCLE);
//Function set: DL=0;Interface is 4 bits data, N=1; 2 Lines, F=0; 5x8 dots font (2 cycle)
LCD_putcmd(0x28,LCD_2CYCLE);
//Display Off: D=0; Display off, C=0; Cursor Off, B=0; Blinking Off (2 cycle)
LCD_putcmd(0x08,LCD_2CYCLE);
//Display Clear (2 cycle)
LCD_putcmd(0x01,LCD_2CYCLE);
//Entry Mode Set: I/D=1; Increment, S=0; No shift (2 cycle)
LCD_putcmd(0x06,LCD_2CYCLE);
}
//------------------------------------------------------------------------------------------------------------------
// Subroutine sends string writes LCD
//------------------------------------------------------------------------------------------------------------------
void LCD_puts(char *s) //Implementation of C puts() function for the LCD, which display
//a terminated null string to the LCD
{
while(*s != 0) //While not Null
{
if (*s == '\n')
LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); //Goto Second Line
else
LCD_putch(*s);
s++;
}
}
//------------------------------------------------------------------------------------------------------------------
// Subroutine implementing integer value from 00 to 99
//------------------------------------------------------------------------------------------------------------------
char *num2str(char number) //Function is used to convert a numeric value to on the LCD
{
unsigned char digit;
digit = '0'; //Start with ASCII '0'
while(number >= 10) //Keep Looping for larger than 10
{
digit++; //Increase ASCII character
number -= 10; //Subtract number with 10
}
sdigit[0]='0'; //Default first Digit to '0'
if (digit != '0')
sdigit[0]=digit; //Put the Second digit
sdigit[1]='0' + number;
return sdigit;
}
//------------------------------------------------------------------------------------------------------------------
// Simple program to read multiple channels AD
//------------------------------------------------------------------------------------------------------------------
/*
MUX3 MUX2 MUX1 MUX0 Pino a ler
0 0 0 0 PORTC0 Analogic0
0 0 0 1 PORTC1 Analogic1
0 0 1 0 PORTC2 Analogic2
0 0 1 1 PORTC3 Analogic3
0 1 0 0 PORTC4 Analogic4
0 1 0 1 PORTC5 Analogic5
*/
int adc_value; //Variable used to store the value read by the ADC
void adc_init (void); //Function to initialize / configure the converter
char buffer[5],mode; //Buffer to the function itoa
uint16_t read_adc (uint8_t channel); //We function to read an arbitrary channel ADC
uint8_t i=0,cont=0; //Variable is used in for
void USART_init (void); //Function to initialize the USART
void USART_send (unsigned char data); //Function that sends a character by the USART
void USART_putstring (char* StringPtr); //Function that sends a string by the USART
void init_IO (void)
{
DDRB |= (1<<PB5);
DDRD = 0xFF; //Set PORTD as Output LCD
PORTD = 0;
}
int Setup(void)
{
for(cont=0; cont<2; cont++)
{
for(i=0; i<4;i++) //Number of channels to be read AD's
{
USART_send('\r');
USART_putstring("Leitura do Canal");
USART_send('\r'); //To change the line in the terminal
USART_send('0' + i); //When we send only a number between 0 and 9 can simply add the number to the character '0 '
USART_putstring(" : "); //Just the thing to beautify
adc_value = read_adc(i); //We read channel of a converter
itoa(adc_value, buffer, 10); //We convert the measured value to a string
USART_putstring(buffer); //We send the same string
USART_putstring(" "); //Just the thing to beautify
_delay_ms(500);
}
USART_send('\r'); //To change the line in the terminal
USART_send('\n');
}
return 0;
}
void adc_init(void)
{
ADCSRA |= ((1<<ADPS2) | (1<<ADPS1)); //11059200/64 = 172,8Khz
ADMUX |= (1<<REFS0); //5V Reference
ADMUX &= ~(1<<REFS1);
ADCSRA |= ((1<<ADEN) | (1<<ADSC)); //ADC on, makes a first conversion circuit to start and why is the slowest
}
uint16_t read_adc(uint8_t channel)
{
ADMUX &= 0xF0; //Clears the previous channel
ADMUX |= channel; //Sets the new channel to read from ADC
ADCSRA |= (1<<ADSC); //Start a new conversion
while(ADCSRA & (1<<ADSC)); //Hopes that the conversion is done
return (ADCL + (ADCH<<8)); //Returns the value of ADC
}
void USART_init(void)
{
UBRR0H = (((F_CPU/BAUD_RATE)/16)-1)>>8; //Set baud rate >>shifts 8 bits right (Asynchronous Normal mode)
UBRR0L = (((F_CPU/BAUD_RATE)/16)-1);
UCSR0B = (1<<RXEN0)|(1<<TXEN0); //(Receiver & Transmitter) Enable Tx & Rx
UCSR0C= (1<<UCSZ01)|(1<<UCSZ00); //(Character Size) Config USART 8N1 (8data 1stop bit)
//UCSR0C = (3<<UCSZ00); //As above
}
void USART_send( unsigned char data)
{
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}
void USART_putstring(char* StringPtr)
{
while(*StringPtr != 0x00)
{
USART_send(*StringPtr);
StringPtr++;
}
}
void start_timer0(void)
{
TCCR0B = _BV(CS02) | _BV(CS00); //Use maximum prescaller: Clk/1024
}
void stop_timer0(void)
{
TCCR0B = 0;
}
void goto_idle(void) {
button_state = IDLE; //goto idle state
press= 0;
TCNT0 = 0;
time = 0;
}
//ISR for Pin Change Interrupt
//Change to reflect Input pin for button
//Make sure there is debounce circuit on button
//inorder to eliminate bouncing.
ISR(PCINT0_vect)
{
stop_timer0(); //stop timer
switch (button_state)
{
case IDLE: //IDLE State
{
if(bit_is_set(PINB,PB0)) //if pb0 press
{
start_timer0();
button_state = IPRESS;
}
break;
}
case IPRESS: //Pressed State
{
if(bit_is_clear(PINB,PB0)) //release
{
if(press == 0 && time >= TIME_5S)
{
goto_idle();
hold5 = 1; //must be cleared in main routine
}
else if(press == 0 && time >= TIME_2S)
{
goto_idle();
hold2 = 1; //must be cleared in main routine
}
else //goto release state
{
button_state = RELEASE;
press++; //Quick press
start_timer0(); //continue counter
}
}
else //Pin state is corrupted
{
goto_idle();
}
break;
}
case RELEASE: //Released state
{
if(bit_is_set(PINB,PB0)) //if pb0 press
{
start_timer0(); //continue counting
button_state = IPRESS;
}
else
{
goto_idle();
}
break;
}
default: //states are messed up
{
goto_idle();
break;
}
}
}
// ISR for Timer overflow. using an 8bit timer requires
// software overflow extension.
ISR(TIMER0_OVF_vect)
{
if(time > TIME_OV)
{
stop_timer0();
goto_idle();
}
else
{
time++;
}
}
void init_uc(void)
{
stop_timer0();
TCNT0 = 0x94; // Start counter from 0x94, overflow at 10 mSec
// Enable Interrupts
TIMSK0 = _BV(TOIE0); // enable overflow interrupt
PCICR = _BV(PCIE0); // enable pcint0
PCMSK0 = _BV(PCINT0);
MCUCR = _BV(PUD);
hold5 = 0;
hold2 = 0;
press = 0;
time = 0;
button_state = IDLE;
sei();
}
//void SetupMode_Write (void)
//{
// printf(" ** AVR 168 Printing and Adjust Data ** \n\n");
//}
int main(void)
{
uint32_t i;
uint8_t x;
init_IO();
USART_init(); //We initialize the USART
adc_init(); //We initialize the ADC
initlcd(); //Initial LCD using 4 bit data interface
LCD_putcmd(0x0C,LCD_2CYCLE); //Display On, Cursor Off
LCD_putcmd(LCD_CLEAR,LCD_2CYCLE); //Clear LCD
init_uc();
for(;;) //Infinite loop
{
if(hold5) //Take action on 5 second hold
{
LCD_putcmd(LCD_CLEAR,LCD_2CYCLE); //Clear LCD
LCD_puts(" Setup Mode ");
Setup();
hold5 = 0;
}
else if(hold2) //Take action on 2 second hold
{
//SetupMode_Write();
hold2 = 0;
}
else if(press > 0) //take action on multiple quick press
{}
else if(press == 0)
{}
for(i=0; i<=100; i++) //put time into loop
{
x = PINC;
}
} // endless loop
return 0;
}
Obrigado