diff --git a/flake.nix b/flake.nix index d672974..82d9a96 100644 --- a/flake.nix +++ b/flake.nix @@ -38,16 +38,14 @@ MCU = "attiny85"; CHIP_SELECT = "AVR_ATtiny85"; F_CPU = "8000000"; - CFLAGS = ''-O -finline-functions -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -DF_CPU=${F_CPU} -std=gnu99 -D__${CHIP_SELECT}__=1 -mmcu=${MCU}''; + CFLAGS = ''-finline-functions -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -DF_CPU=${F_CPU} -std=gnu99 -D__${CHIP_SELECT}__=1 -mmcu=${MCU}''; buildPhase = '' - ${avr.gcc}/bin/avr-gcc ${CFLAGS} -I${src}/base/include/ -E -o main.post-cc ${src}/pwm/src/main.c - ${avr.gcc}/bin/avr-gcc ${CFLAGS} -I${src}/base/include/ -o main.elf ${src}/pwm/src/main.c + ${avr.gcc}/bin/avr-gcc ${CFLAGS} -I${pkgs.simavr}/include/ -I${src}/base/include/ -o main.elf ${src}/ws2812/src/main.c $OBJCOPY -O ihex main.elf main.hex ''; installPhase = '' mkdir $out - cp main.post-cc $out cp main.elf main.hex $out ''; }; @@ -60,6 +58,7 @@ gcc avrdude simavr + gtkwave ]; in pkgs.mkShell { diff --git a/flash-trinket b/flash-trinket index 94b7404..e19b36e 100755 --- a/flash-trinket +++ b/flash-trinket @@ -1 +1,2 @@ -avrdude -c usbtiny -p attiny85 -U flash:w:$1:i; sleep 5; avrdude -c usbtiny -p attiny85 -D -U flash:w:$1:i \ No newline at end of file +avrdude -c usbtiny -p attiny85 -U flash:w:$1:i; sleep 5; avrdude -c usbtiny -p attiny85 -D -U flash:w:$1:i +# avrdude -c usbtiny -p attiny85 -D -U flash:w:$1:i diff --git a/ws2812/src/direct_write_assembly.c b/ws2812/src/direct_write_assembly.c new file mode 100644 index 0000000..58d02c2 --- /dev/null +++ b/ws2812/src/direct_write_assembly.c @@ -0,0 +1,42 @@ +// I have reduced noops at the end of each of these to take into account that there are several additional clock ticks of setup, after. However, I'm not totally sure that I get things right, seeing that there are four possible sequences, and I'm not really accounting for the timing of all four of them. +#define write_zero(port, bit) \ + __asm__ __volatile__ ( \ + "sbi %0, %1" "\n\t" \ + "nop" "\n\t" \ + "cbi %0, %1" "\n\t" \ + "nop" "\n\t" \ + : /* no outputs */ \ + : "I" (_SFR_IO_ADDR(port)), \ + "I" (bit) \ + ) + +#define write_one(port, bit) \ + __asm__ __volatile__ ( \ + "sbi %0, %1" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "cbi %0, %1" "\n\t" \ + : /* no outputs */ \ + : "I" (_SFR_IO_ADDR(port)), \ + "I" (bit) \ + ) + +#define write_byte(port, bit, byte) \ + __asm__ __volatile__ ( \ + "ldi %z, 8" "\n\t" \ // count out eight bits + "ld __tmp_reg__, %[byte]" "\n\t" \ // load the current byte into a temporary register + "L_%=: " "lsl __tmp_reg__" "\n\t" \ // shift the temporary register left, saving the msb in SREG (1 cycle) + "brbs I_%=" "\n\t" \ // if SREG is set, branch to I_%= (2 cycles if true, 1 cycle if false) + write_zero(port, bit) \ // SREG was zero, so write a zero to the port + "rjmp J_%=" "\n\t" \ // Jump to J_%=, the loop cleanup (2 cycles) + "I_%=: " write_one(port, bit) \ // SREG was one, so write a one to the port + "J_%=: " "dec %z" "\n\t" \ // Decrement the bits counter (1 cycle) + "cpi %z, 0" "\n\t" \ // are there any bits left to send? (1 cycle) + "brne L_%=" "\n\t" \ // there are, so go back to L_%= (2 cycles) + : /* no outputs */ \ + : [byte] "I" (byte) \ + ) + diff --git a/ws2812/src/main.c b/ws2812/src/main.c new file mode 100644 index 0000000..091dbd2 --- /dev/null +++ b/ws2812/src/main.c @@ -0,0 +1,94 @@ +#include +#include +#include "ws2812.h" +#include "np_common.c" + +/* +#include +AVR_MCU(F_CPU, "attiny85"); + +const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = { + { AVR_MCU_VCD_SYMBOL("GTCCR"), .what = (void*)>CCR, }, + // { AVR_MCU_VCD_SYMBOL("TCCR0B"), .what = (void*)&TCCR0B, }, + { AVR_MCU_VCD_SYMBOL("TIMSK"), .what = (void*)&TIMSK, }, + { AVR_MCU_VCD_SYMBOL("TIFR"), .what = (void*)&TIFR, }, + { AVR_MCU_VCD_SYMBOL("OCR0A"), .what = (void*)&OCR0A, }, + { AVR_MCU_VCD_SYMBOL("OCR0B"), .what = (void*)&OCR0B, }, + { AVR_MCU_VCD_SYMBOL("current"), .what = (void*)¤t, }, + { AVR_MCU_VCD_SYMBOL("idx"), .what = (void*)&idx, }, + { AVR_MCU_VCD_SYMBOL("cnt"), .what = (void*)&cnt, }, + { AVR_MCU_VCD_SYMBOL("val"), .what = (void*)&val, }, + // { AVR_MCU_VCD_SYMBOL("DDRB"), .what = (void*)&DDRB, }, + { AVR_MCU_VCD_SYMBOL("PORTB"), .what = (void*)&PORTB, }, + // { AVR_MCU_VCD_SYMBOL("TCNT0"), .what = (void*)&TCNT0, }, + // { AVR_MCU_VCD_SYMBOL("TCNT1"), .what = (void*)&TCNT1, }, +}; +*/ + +#define PIXEL_COUNT 7 +const uint8_t pixels_1[PIXEL_COUNT * 4] = + { 0, 0, 0, 0, + 32, 0, 0, 0, + 64, 0, 0, 0, + 96, 0, 0, 0, + 128, 0, 0, 0, + 160, 0, 0, 0, + 192, 0, 0, 0 }; + +const uint8_t pixels_2[PIXEL_COUNT * 4] = + { 0, 0, 0, 0, + 0, 32, 0, 0, + 0, 64, 0, 0, + 0, 96, 0, 0, + 0, 128, 0, 0, + 0, 160, 0, 0, + 0, 192, 0, 0 }; + +const uint8_t pixels_3[PIXEL_COUNT * 4] = + { 0, 0, 0, 0, + 0, 0, 32, 0, + 0, 0, 64, 0, + 0, 0, 96, 0, + 0, 0, 128, 0, + 0, 0, 160, 0, + 0, 0, 192, 0 }; + +const uint8_t pixels_4[PIXEL_COUNT * 4] = + { 0, 0, 0, 0, + 0, 0, 0, 32, + 0, 0, 0, 64, + 0, 0, 0, 96, + 0, 0, 0, 128, + 0, 0, 0, 160, + 0, 0, 0, 192 }; + +void blink(void) { + PORTB |= _BV(2); + _delay_ms(100); + PORTB &= ~(_BV(2)); +} + +int main (void) { + _delay_ms(1000); + + PORTB = 0; + DDRB = _BV(0) | _BV(1) | _BV(2); + _delay_ms(50); + + blink(); + np_initialize(); + sei(); + + _delay_ms(500); + + while (1) { + blink(); + write_pixels(pixels_1, PIXEL_COUNT * 4); + _delay_ms(1000); + write_pixels(pixels_2, PIXEL_COUNT * 4); + _delay_ms(1000); + write_pixels(pixels_3, PIXEL_COUNT * 4); + _delay_ms(1000); + write_pixels(pixels_4, PIXEL_COUNT * 4); + } +} diff --git a/ws2812/src/np_common.c b/ws2812/src/np_common.c new file mode 100644 index 0000000..7968c41 --- /dev/null +++ b/ws2812/src/np_common.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#include "ws2812.h" + +/* +void latch(io_pin_t *addr) { + if (addr->bit >= 8) return; + *(addr->port) &= ~(1<<0); + _delay_us(50); +} +*/ + +#define T0_HIGH 4 +#define T1_HIGH 7 +#define T_FRAME 11 + +volatile uint8_t bit = 0; +volatile uint8_t val = 0; +volatile uint32_t idx = 0; +volatile uint8_t sub_idx = 0; +volatile const uint8_t *data = NULL; +volatile uint32_t cnt = 0; + +ISR(TIMER0_OVF_vect) { + // If we have sent all of the bytes + if (idx >= cnt) { + // Turn off the clock + TCCR0B &= ~(_BV(CS00)); + // Disconnect OC0A and OC0B + TCCR0A &= ~(_BV(COM0A1) | _BV(COM0A0) | _BV(COM0B1) | _BV(COM0B0)); + // Write a 0 to the PORTB[1] / OC0B + PORTB &= ~(_BV(1)); + + // After this, the timer should be stopped and we shouldn't see any further interrupts. + return; + } + + // if the current bit is high, spend T1_HIGH ticks with the line asserted. + // Otherwise, assert for T0_HIGH ticks. + OCR0B = bit ? T1_HIGH : T0_HIGH; + + // Now that we have the timer set correctly, let's preload the next bit + + // If we're at the last bit for the curret byte, let's move on to the next byte + if (sub_idx == 8) { + sub_idx = 0; + idx++; + val = data[idx]; + } + + // Load the bit from the current value + bit = val & _BV(sub_idx); + + // And then increment to the next bit for the next iteration + sub_idx++; +} + +void write_pixels(const uint8_t *pixels, uint8_t length) { + data = pixels; + cnt = length; + + // Set OC0B to Fast PWM, Clear on Match, Set on Bottom mode + TCCR0A |= _BV(COM0B1); + + // Enable the clock + TCCR0B |= _BV(CS00); + + // Now wait until the clock gets turned off. + while (TCCR0B & _BV(CS00)) { } +} + +void np_initialize() { + // latch(addr); + + TCCR0B = _BV(WGM02); + TCCR0A = _BV(WGM01) | _BV(WGM00); + TIMSK = _BV(TOIE0); + OCR0A = T_FRAME; +} + diff --git a/ws2812/src/np_rgb.c b/ws2812/src/np_rgb.c new file mode 100644 index 0000000..23e4658 --- /dev/null +++ b/ws2812/src/np_rgb.c @@ -0,0 +1,5 @@ +#include "np_common.c" + +void np_write_rgb(io_pin_t *addr, rgb_t *values, uint8_t length) { + write_pixels(addr, values, length * 3); +} diff --git a/ws2812/src/ws2812.h b/ws2812/src/ws2812.h new file mode 100644 index 0000000..9f5656b --- /dev/null +++ b/ws2812/src/ws2812.h @@ -0,0 +1,28 @@ +#ifndef __neopixels_h__ +#define __neopixels_h__ + +#include +#include + +typedef struct RGB_s { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb_t; + +typedef struct RGBW_s { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t w; +} rgbw_t; + +void np_initialize(); +void np_write_rgb(io_pin_t *addr, rgb_t *values, uint8_t length); +void write_pixels(const uint8_t *pixels, uint8_t length); +// void np_write_grb(io_pin_t *addr, rgb_t *values, uint8_t length); +// void np_write_rgbw(io_pin_t *addr, rgbw_t *values, uint8_t length); +// void np_write_grbw(io_pin_t *addr, rgbw_t *values, uint8_t length); + +#endif +