-
-
Save anishathalye/a2e7e2bb7718dc1c2a4ddbdcc317898a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// blinking LED | |
// | |
// set lfuse to 0x5E for 20 MHz xtal | |
// 115200 baud serial | |
// | |
// Anish Athalye | |
// | |
// some code from http://academy.cba.mit.edu/classes/embedded_programming/hello.ftdi.44.echo.c | |
// license for that: | |
// | |
// Neil Gershenfeld | |
// 12/8/10 | |
// | |
// (c) Massachusetts Institute of Technology 2010 | |
// This work may be reproduced, modified, distributed, | |
// performed, and displayed for any purpose. Copyright is | |
// retained and must be preserved. The work is provided | |
// as is; no warranty is provided, and users accept all | |
// liability. | |
#include <avr/io.h> | |
#include <util/delay.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
#define LEDPORT (PORTA) | |
#define LEDDDR (DDRA) | |
#define LED_R (PA2) | |
#define LED_G (PA3) | |
#define LED_B (PA7) | |
#define SETB(byte, bit) (byte |= (1 << bit)) | |
#define CLRB(byte, bit) (byte &= (~ (1 << bit))) | |
#define ASSIGNB(test, byte, bit) do { if (test) SETB(byte, bit); else CLRB(byte, bit); } while (0); | |
int pwm_ctr = 0; | |
int pwm_r = 0; | |
int pwm_g = 0; | |
int pwm_b = 0; | |
#define PWM_VALUES (20) // must be <= 0b00111111 | |
// pwm_{color} values can go from [0, PWM_VALUES] | |
// 0 is off, and PWM_VALUES is full brightness | |
#define PWM_HZ (50) | |
#define PWM_TICKS (PWM_VALUES * PWM_HZ) | |
#define output(directions,pin) (directions |= pin) // set port direction for output | |
#define set(port,pin) (port |= pin) // set port pin | |
#define clear(port,pin) (port &= (~pin)) // clear port pin | |
#define pin_test(pins,pin) (pins & pin) // test for port pin | |
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set | |
#define bit_delay_time 8.5 // bit delay for 115200 with overhead | |
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay | |
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay | |
#define char_delay() _delay_ms(10) // char delay | |
#define serial_port PORTA | |
#define serial_direction DDRA | |
#define serial_pins PINA | |
#define serial_pin_in (1 << PA0) | |
#define serial_pin_out (1 << PA1) | |
void get_char(volatile unsigned char *pins, unsigned char pin, unsigned char *rxbyte) | |
{ | |
// | |
// read character into rxbyte on pins pin | |
// assumes line driver (inverts bits) | |
// | |
*rxbyte = 0; | |
while (pin_test(*pins,pin)) | |
// | |
// wait for start bit | |
// | |
; | |
cli(); // disable interrupts | |
// | |
// delay to middle of first data bit | |
// | |
half_bit_delay(); | |
bit_delay(); | |
// | |
// unrolled loop to read data bits | |
// | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 0); | |
else | |
*rxbyte |= (0 << 0); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 1); | |
else | |
*rxbyte |= (0 << 1); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 2); | |
else | |
*rxbyte |= (0 << 2); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 3); | |
else | |
*rxbyte |= (0 << 3); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 4); | |
else | |
*rxbyte |= (0 << 4); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 5); | |
else | |
*rxbyte |= (0 << 5); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 6); | |
else | |
*rxbyte |= (0 << 6); | |
bit_delay(); | |
if pin_test(*pins,pin) | |
*rxbyte |= (1 << 7); | |
else | |
*rxbyte |= (0 << 7); | |
// | |
// wait for stop bit | |
// | |
bit_delay(); | |
half_bit_delay(); | |
sei(); // reenable interrupts | |
} | |
int main(void) | |
{ | |
// set clock divider to /1 | |
CLKPR = (1 << CLKPCE); | |
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); | |
// configure timer1 for CTC (clear timer on compare) mode | |
TCCR1B |= (1 << WGM12); | |
// enable the CTC interrupt | |
TIMSK1 |= (1 << OCIE1A); | |
// enable global interrupts | |
sei(); | |
// start the timer at 20MHz / 1024 | |
TCCR1B |= ((1 << CS10) | (1 << CS12)); | |
// set the CTC compare value | |
// timer is incrementing at 20MHz / 1024 = 19531.25 Hz | |
// if want 100Hz pwm for example, the timer max count should be set to | |
// 19531.25 Hz / 100 Hz = 195 | |
OCR1A = 19531.25 / PWM_TICKS; | |
// set to output port | |
LEDDDR |= (1 << LED_R | 1 << LED_G | 1 << LED_B); | |
// make everything off | |
SETB(LEDPORT, LED_G); | |
SETB(LEDPORT, LED_R); | |
SETB(LEDPORT, LED_B); | |
unsigned char input, color, value; | |
while (1) | |
{ | |
get_char(&serial_pins, serial_pin_in, &input); | |
// protocol: c = CCVVVVVV | |
// where CC is the color channel | |
// and VV is the value | |
// CC: 0 = R, 1 = G, 2 = B, 3 = nothing | |
// VVVVVV: value from 0x00 to PWM_VALUES | |
color = input >> 6; | |
value = input & 0x3f; | |
if (0 <= value && value <= PWM_VALUES) | |
{ | |
if (color == 0) | |
pwm_r = value; | |
if (color == 1) | |
pwm_g = value; | |
if (color == 2) | |
pwm_b = value; | |
} | |
}; | |
} | |
ISR(TIM1_COMPA_vect) | |
{ | |
if (pwm_ctr >= pwm_r) | |
{ | |
SETB(LEDPORT, LED_R); // off | |
} | |
else | |
{ | |
CLRB(LEDPORT, LED_R); // on | |
} | |
if (pwm_ctr >= pwm_g) | |
{ | |
SETB(LEDPORT, LED_G); // off | |
} | |
else | |
{ | |
CLRB(LEDPORT, LED_G); // on | |
} | |
if (pwm_ctr >= pwm_b) | |
{ | |
SETB(LEDPORT, LED_B); // off | |
} | |
else | |
{ | |
CLRB(LEDPORT, LED_B); // on | |
} | |
pwm_ctr++; | |
if (pwm_ctr >= PWM_VALUES) | |
{ | |
pwm_ctr = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment