Permalink
Browse files

Added led_strip2.c and led_strip3.c. Improved the Makefile.

  • Loading branch information...
1 parent ec6426f commit e8bc1cd609eca515f48a88b4ac527dd1f10e3919 @DavidEGrayson DavidEGrayson committed Mar 16, 2012
Showing with 325 additions and 12 deletions.
  1. +3 −1 .gitignore
  2. +13 −10 Makefile
  3. +6 −1 led_strip.c
  4. +141 −0 led_strip2.c
  5. +162 −0 led_strip3.c
View
4 .gitignore
@@ -1,3 +1,5 @@
*.o
*.hex
-*.obj
+*.obj
+*.lss
+*.map
View
23 Makefile
@@ -5,23 +5,26 @@ PORT = \\\\.\USBSER000
CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) $(DEVICE_SPECIFIC_CFLAGS) -Os
CC=avr-gcc
-OBJ2HEX=avr-objcopy
-LDFLAGS=-Wl,-gc-sections -Wl,-relax
+OBJCOPY=avr-objcopy
+OBJDUMP=avr-objdump
+LDFLAGS=-Wl,-gc-sections -Wl,-relax -Wl,-Map="$(@:%.elf=%.map)"
AVRDUDE=avrdude
TARGET=led_strip
-OBJECT_FILES=led_strip.o
-all: $(TARGET).hex
+all: $(TARGET).hex $(TARGET).lss
clean:
- rm -f *.o *.hex *.obj
+ rm -f *.o *.hex *.elf *.map *.lss
-%.hex: %.obj
- $(OBJ2HEX) -R .eeprom -O ihex $< $@
+%.hex: %.elf
+ $(OBJCOPY) -R .eeprom -O ihex $< $@
-%.obj: $(OBJECT_FILES)
- $(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@
+%.lss: %.elf
+ $(OBJDUMP) -h -S $< > $@
+
+%.elf: %.o
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
program: $(TARGET).hex
- $(AVRDUDE) -p $(AVRDUDE_DEVICE) -c avrisp2 -P $(PORT) -U flash:w:$(TARGET).hex
+ $(AVRDUDE) -p $(AVRDUDE_DEVICE) -c avrisp2 -P $(PORT) -U flash:w:$<
View
7 led_strip.c
@@ -35,9 +35,14 @@ typedef struct rgb_color
The colors parameter should point to an array of rgb_color structs that hold the colors to send.
The count parameter is the number of colors to send.
- This function takes about 2 ms to update 30 LEDs.
+ This function takes less than 2 ms to update 30 LEDs.
Interrupts must be disabled during that time, so any interrupt-based library
can be negatively affected by this function.
+
+ Timing details at 20 MHz (the numbers slightly different at 16 MHz):
+ 0 pulse = 700 ns
+ 1 pulse = 1300 ns
+ "period" = 2500 ns
*/
void __attribute__((noinline)) led_strip_write(rgb_color * colors, unsigned int count)
{
View
141 led_strip2.c
@@ -0,0 +1,141 @@
+/* This is AVR code for driving the RGB LED strips from Pololu.
+ This version can actually drive two LED strips at the same time.
+ For a simpler version with more comments that does one LED strip at a time,
+ see led_strip.c.
+ This version only supports 20 MHz processors.
+ */
+
+#define F_CPU 20000000
+
+#define LED_STRIP1_PORT PORTC
+#define LED_STRIP1_DDR DDRC
+#define LED_STRIP1_PIN 0
+
+#define LED_STRIP2_PORT PORTC
+#define LED_STRIP2_DDR DDRC
+#define LED_STRIP2_PIN 1
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <math.h>
+
+typedef struct rgb_color
+{
+ unsigned char red, green, blue;
+} rgb_color;
+
+/** The timing of this function is the same as led_strip_write() in led_strip.c except
+ it does two chains of LED strips simultaneously.
+ Updating 2*30 LEDs takes less than 2 ms. **/
+void __attribute__((noinline)) led_strip_write2(rgb_color * colors1, rgb_color * colors2, unsigned int count)
+{
+ LED_STRIP1_PORT &= ~(1<<LED_STRIP1_PIN);
+ LED_STRIP1_DDR |= (1<<LED_STRIP1_PIN);
+
+ LED_STRIP2_PORT &= ~(1<<LED_STRIP2_PIN);
+ LED_STRIP2_DDR |= (1<<LED_STRIP2_PIN);
+
+ cli(); // Disable interrupts temporarily because we don't want our pulse timing to be messed up.
+ while(count--)
+ {
+ unsigned char b1, b2; // brightness values
+
+ // Send a color to the LED strip.
+ // The assembly below also increments the 'colors' pointer,
+ // it will be pointing to the next color at the end of this loop.
+ asm volatile(
+ "rcall send_led_strip_byte%=\n" // Send red component.
+ "rcall send_led_strip_byte%=\n" // Send green component.
+ "rcall send_led_strip_byte%=\n" // Send blue component.
+ "rjmp led_strip_asm_end%=\n" // Jump past the assembly subroutines.
+
+ // send_led_strip_byte subroutine: Sends a byte to the LED strip.
+ "send_led_strip_byte%=:\n"
+ "ld %2, %a0+\n" // Read the next color brightness byte and advance the pointer
+ "ld %3, %a1+\n" // Read the next color brightness byte and advance the pointer
+ "rcall send_led_strip_bit%=\n" // Send most-significant bit (bit 7).
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n" // Send least-significant bit (bit 0).
+ "ret\n"
+
+ // send_led_strip_bit subroutine: Sends single bit to the LED strip by driving the data line
+ // high for some time. The amount of time the line is high depends on whether the bit is 0 or 1,
+ // but this function always takes the same time (2 us).
+ "send_led_strip_bit%=:\n"
+ "sbi %6, %7\n" // Drive the line high.
+ "nop\n" "nop\n" "nop\n" "nop\n"
+
+ "sbi %8, %9\n" // Drive the line2 high.
+ "nop\n" "nop\n" "nop\n" "nop\n"
+
+ "rol %2\n" // Rotate left through carry.
+ "brcs .+2\n" "cbi %6, %7\n" // If the bit to send is 0, drive the line low now.
+ "brcc .+4\n" "nop\n" "nop\n" // Fix the timing.
+
+ "rol %3\n" // Rotate left through carry.
+ "brcs .+2\n" "cbi %8, %9\n" // If the bit to send is 0, drive the line low now.
+ "brcc .+4\n" "nop\n" "nop\n" // Fix the timing.
+
+ "nop\n" "nop\n"
+
+ "cbi %6, %7\n"
+ "nop\n" "nop\n" "nop\n" "nop\n"
+
+ "cbi %8, %9\n"
+ "nop\n" "nop\n" "nop\n" "nop\n" "nop\n"
+ "nop\n" "nop\n" "nop\n" "nop\n"
+
+ "ret\n"
+ "led_strip_asm_end%=: "
+ : "=b" (colors1),
+ "=b" (colors2),
+ "=&r" (b1), // %2 temporarily holds the brightness of strip1
+ "=&r" (b2) // %3 temporarily holds the brightness of strip2
+ : "0" (colors1), // %a0 points to the next color to display
+ "1" (colors2), // %a1 points to the next color to display for the other strip
+ "I" (_SFR_IO_ADDR(LED_STRIP1_PORT)), // %6 is the port register (e.g. PORTC)
+ "I" (LED_STRIP1_PIN), // %7 is the pin number (0-8)
+ "I" (_SFR_IO_ADDR(LED_STRIP2_PORT)), // %8 is the port registers
+ "I" (LED_STRIP2_PIN) // %9 is the pin number
+ );
+
+ // Uncomment the line below to temporarily enable interrupts between each color.
+ //sei(); asm volatile("nop\n"); cli();
+ }
+ sei(); // Re-enable interrupts now that we are done.
+ _delay_us(24); // Hold the line low for 15 microseconds to send the reset signal.
+}
+
+#define LED_COUNT 60
+rgb_color colors1[LED_COUNT], colors2[LED_COUNT];
+
+int main()
+{
+ unsigned int time = 0;
+
+ while(1)
+ {
+ unsigned int i;
+ for(i = 0; i < LED_COUNT; i++)
+ {
+ unsigned char x = (time >> 2) - 8*i;
+ colors1[i] = (rgb_color){ x, 255 - x, x };
+
+ x = (time >> 2) - 50*i;
+ if (x > 127){ x = 255 - x; }
+ x = x*x >> 8;
+ colors2[i] = (rgb_color){ 0, 0, 2*x };
+ }
+
+ led_strip_write2(colors1, colors2, LED_COUNT);
+
+ _delay_ms(20);
+ time += 20;
+ }
+}
View
162 led_strip3.c
@@ -0,0 +1,162 @@
+/* This is AVR code for driving the RGB LED strips from Pololu.
+ This version can actually drive three LED strips at the same time.
+ For a simpler version with more comments that does one LED strip at a time,
+ see led_strip.c.
+ This version only supports 20 MHz processors.
+ */
+
+#define F_CPU 20000000
+
+#define LED_STRIP1_PORT PORTC
+#define LED_STRIP1_DDR DDRC
+#define LED_STRIP1_PIN 0
+
+#define LED_STRIP2_PORT PORTC
+#define LED_STRIP2_DDR DDRC
+#define LED_STRIP2_PIN 1
+
+#define LED_STRIP3_PORT PORTD
+#define LED_STRIP3_DDR DDRD
+#define LED_STRIP3_PIN 0
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <math.h>
+
+typedef struct rgb_color
+{
+ unsigned char red, green, blue;
+} rgb_color;
+
+/** The timing of this function is the same as led_strip_write() in led_strip.c except
+ it does two chains of LED strips simultaneously.
+ Updating 3*30 LEDs takes less than 2 ms. **/
+void __attribute__((noinline)) led_strip_write3(rgb_color * colors1, rgb_color * colors2, rgb_color * colors3, unsigned int count)
+{
+ LED_STRIP1_PORT &= ~(1<<LED_STRIP1_PIN);
+ LED_STRIP1_DDR |= (1<<LED_STRIP1_PIN);
+
+ LED_STRIP2_PORT &= ~(1<<LED_STRIP2_PIN);
+ LED_STRIP2_DDR |= (1<<LED_STRIP2_PIN);
+
+ LED_STRIP3_PORT &= ~(1<<LED_STRIP3_PIN);
+ LED_STRIP3_DDR |= (1<<LED_STRIP3_PIN);
+
+ cli(); // Disable interrupts temporarily because we don't want our pulse timing to be messed up.
+ while(count--)
+ {
+ unsigned char b1, b2, b3; // brightness values
+
+ // Send a color to the LED strip.
+ // The assembly below also increments the 'colors' pointer,
+ // it will be pointing to the next color at the end of this loop.
+ asm volatile(
+ "rcall send_led_strip_byte%=\n" // Send red component.
+ "rcall send_led_strip_byte%=\n" // Send green component.
+ "rcall send_led_strip_byte%=\n" // Send blue component.
+ "rjmp led_strip_asm_end%=\n" // Jump past the assembly subroutines.
+
+ // send_led_strip_byte subroutine: Sends a byte to the LED strip.
+ "send_led_strip_byte%=:\n"
+ "ld %3, %a0+\n" // strip1: Read the next color brightness byte and advance the pointer
+ "ld %4, %a1+\n" // strip2
+ "ld %5, %a2+\n" // strip3
+ "rcall send_led_strip_bit%=\n" // Send most-significant bit (bit 7).
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n"
+ "rcall send_led_strip_bit%=\n" // Send least-significant bit (bit 0).
+ "ret\n"
+
+ // send_led_strip_bit subroutine: Sends single bit to the LED strip by driving the data line
+ // high for some time. The amount of time the line is high depends on whether the bit is 0 or 1,
+ // but this function always takes the same time (2 us).
+ "send_led_strip_bit%=:\n"
+ "sbi %9, %10\n" // Strip1: Drive high.
+ "nop\n" "nop\n" "nop\n"
+
+ "sbi %11, %12\n" // Strip2: Drive high.
+ "nop\n" "nop\n"
+
+ "rol %3\n" // Strip1: Rotate brightness left through carry.
+ "sbi %13, %14\n" // Strip3: Drive high.
+ "brcs .+2\n" "cbi %9, %10\n" // Strip1: If the bit to send is 0, drive low.
+ "brcc .+4\n" "nop\n" "nop\n" // Fix the timing.
+
+ "rol %4\n" // Strip 2: Rotate left through carry.
+ "brcs .+2\n" "cbi %11, %12\n" // Strip 2: If the bit to send is 0, drive low.
+ "brcc .+4\n" "nop\n" "nop\n" // Fix the timing.
+
+ "rol %5\n" // Strip 3: Rotate left through carry.
+ "brcs .+4\n" "cbi %13, %14\n" "cbi %9, %10\n" // Strip 3: If the bit to send is 0, drive low.
+ "brcc .+6\n" "cbi %9, %10\n" "nop\n" "nop\n" // We also drive strip 1 low and fix the timing.
+
+ "cbi %11, %12\n" // Strip 2: Drive low
+ "nop\n" "nop\n" "nop\n"
+
+ "cbi %13, %14\n" // Strip 3: Drive low.
+ "nop\n" "nop\n" "nop\n" "nop\n" "nop\n"
+
+ "ret\n"
+ "led_strip_asm_end%=: "
+ : "=e" (colors1),
+ "=e" (colors2),
+ "=e" (colors3),
+ "=&r" (b1), // %3 temporarily holds the brightness of strip1
+ "=&r" (b2), // %4 temporarily holds the brightness of strip2
+ "=&r" (b3) // %5 temporarily holds the brightness of strip3
+ : "0" (colors1), // %a0 points to the next color for strip1
+ "1" (colors2), // %a1 points to the next color for strip2
+ "2" (colors3), // %a2 points to the next color for strip3
+ "I" (_SFR_IO_ADDR(LED_STRIP1_PORT)), // %9 is a port register (e.g. PORTC)
+ "I" (LED_STRIP1_PIN), // %10 is a pin number (0-8)
+ "I" (_SFR_IO_ADDR(LED_STRIP2_PORT)), // %11 is a port register
+ "I" (LED_STRIP2_PIN), // %12 is a pin number
+ "I" (_SFR_IO_ADDR(LED_STRIP3_PORT)), // %13 is a port register
+ "I" (LED_STRIP3_PIN) // %14 is a pin number
+ );
+
+ // Uncomment the line below to temporarily enable interrupts between each color.
+ //sei(); asm volatile("nop\n"); cli();
+ }
+ sei(); // Re-enable interrupts now that we are done.
+ _delay_us(24); // Hold the line low for 15 microseconds to send the reset signal.
+}
+
+#define LED_COUNT 60
+rgb_color colors1[LED_COUNT], colors2[LED_COUNT], colors3[LED_COUNT];
+
+int main()
+{
+ unsigned int time = 0;
+
+ while(1)
+ {
+ unsigned int i;
+ for(i = 0; i < LED_COUNT; i++)
+ {
+ unsigned char x;
+
+ x = (time >> 2) - 8*i;
+ colors1[i] = (rgb_color){ x, 255 - x, x };
+
+ x = (time >> 2) - 50*i;
+ if (x > 127){ x = 255 - x; }
+ x = (x*x) >> 8;
+ colors2[i] = (rgb_color){ 0, 2*x, x };
+
+ x = (time >> 2) - 30*i;
+ if (x > 127){ x = 255 - x; }
+ colors3[i] = (rgb_color){ (x*x) >> 8, 0, ((128-x)*(128-x)) >> 8};
+ }
+
+ led_strip_write3(colors1, colors2, colors3, LED_COUNT);
+
+ _delay_ms(20);
+ time += 20;
+ }
+}

0 comments on commit e8bc1cd

Please sign in to comment.