Save my attempts to do ws2812s via the counter/timer

Savanni D'Gerinel 2022-05-01 09:21:37 -04:00
parent 02716cc58b
commit a383e956ae
7 changed files with 257 additions and 5 deletions

View File

@ -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 {

View File

@ -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
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

View File

@ -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) \
)

94
ws2812/src/main.c Normal file
View File

@ -0,0 +1,94 @@
#include <avr/io.h>
#include <util/delay.h>
#include "ws2812.h"
#include "np_common.c"
/*
#include <simavr/avr/avr_mcu_section.h>
AVR_MCU(F_CPU, "attiny85");
const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = {
{ AVR_MCU_VCD_SYMBOL("GTCCR"), .what = (void*)&GTCCR, },
// { 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*)&current, },
{ 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);
}
}

83
ws2812/src/np_common.c Normal file
View File

@ -0,0 +1,83 @@
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stddef.h>
#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;
}

5
ws2812/src/np_rgb.c Normal file
View File

@ -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);
}

28
ws2812/src/ws2812.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __neopixels_h__
#define __neopixels_h__
#include <avr/io.h>
#include <base.h>
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