#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define DISPLAY PORTD
#define F_CPU 16000000UL
#define set_bit(port, bit) (port |= (1 << bit))
#define clr_bit(port, bit) (port &= ~(1 << bit))
unsigned char table[] = {0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x80, 0x18, 0x06, 0x2F};
long click_dec = 0; // momento do último click do botão A
long click_inc = 0; // momento do último click do botão B
char last_dec = (1 << PC0); // último estado do botão A
char last_inc = (1 << PC1); // último estado do botão B
long timer = 0; // armazena o tempo do timer
int deg = 0; // armazena o ângulo do servo-motor
unsigned char digits[3] = {0, 0, 0}; // armazena dígitos do grau
unsigned char disp = 0; // indica qual display deve ser ativado
// realiza a leitura dos botões, altera e limita a angulação do servo
void read_buttons()
{
// leitura dos pinos dos botões
char read_inc = PINC & (1 << PC1);
char read_dec = PINC & (1 << PC0);
// debounce do botão de decremento
if (read_dec != last_dec && (timer - click_dec) > 10)
{
click_dec = timer;
if(read_dec == 0) {
OCR1A -= 125; // decrementa PWM para mudança da rotação do servo motor
deg -= 6; // decrementa a quantidade de graus equivalente à mudança no pwm
if (OCR1A < 1000) { // mantém OCR1A dentro do intervalo de valores válidos do servo
OCR1A = 1000;
deg = 0;
}
}
last_dec = read_dec;
}
// debaunce do botão de incremento
if (read_inc != last_inc && (timer - click_inc) > 10)
{
click_inc = timer;
if (read_inc == 0) {
OCR1A += 125;
deg += 6;
if (OCR1A > 5000) {
OCR1A = 5000;
deg = 180;
}
}
last_inc = read_inc;
}
// decomposição dos digitos do grau de angulação para enviar pros displays posteriormente
digits[0] = (int)(deg / 100);
digits[1] = (int)(deg / 10) % 10;
digits[2] = (int)(deg % 10);
}
void setup()
{
// coloca pinos dos displays como saída
DDRC |= (1 << PC3) | (1 << PC4) | (1 << PC5);
// coloca pinos dos botões como entrada
DDRC &= ~(1 << PC0) & ~(1 << PC1);
// PORTD como saída
DDRD |= 0b01111111;
PORTC = 0b00000011;
}
void loop()
{
// configura o timer0 para um modo de operação específico, o CTC (Clear Timer on Compare Match)
TCCR0A |= (1<<WGM01);
// habilita a interrupção específica relacionada à comparação A do timer0
TIMSK0 |= (1<<OCIE0A);
// definindo um ponto de comparação
OCR0A = 155;
sei();
DDRB = 0b00000110; // configura os pinos OC1A e OC1B (PB1 e PB2) como saídas
PORTB = 0b11111001;
ICR1 = 39999; // 20ms (parte baixa do pwm)
// configura o TC1 para o modo PWM rápido através do ICR1
TCCR1A = (1 << WGM11);
TCCR1B = (1 << WGM13) | (1<<WGM12) | (1 << CS11);
set_bit(TCCR1A, COM1A1); // ativa o PWM no OC1B (modo de comparação não-invertido)
set_bit(TCCR1A, COM1B1);
OCR1A = 1092;
while (1)
{
read_buttons();
delay(1); // Only for simulation
}
}
ISR(TIMER0_COMPA_vect)
{
timer += 10;
disp++;
disp %= 3;
PORTD = 0xFF;
// atualiza índice dos displays
if (disp == 2) {
PORTC &= ~(1 << PC3);
PORTC &= ~(1 << PC4);
PORTC |= (1 << PC5);
}
else if (disp == 1) {
PORTC &= ~(1 << PC3);
PORTC &= ~(1 << PC5);
PORTC |= (1 << PC4);
}
else if (disp == 0) {
PORTC &= ~(1 << PC4);
PORTC &= ~(1 << PC5);
PORTC |= (1 << PC3);
}
DISPLAY = table[digits[disp]];
}