Compare commits

...

5 Commits
main ... ws2812

7 changed files with 315 additions and 4 deletions

View File

@ -284,6 +284,28 @@
];
};
packages."x86_64-linux"."ws2812_" =
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
avr = pkgs.pkgsCross.avr.buildPackages;
in packages."x86_64-linux"."ws2812" { gcc = "${avr.gcc}/bin/avr-gcc"; cflags = mcu_cflags attiny85; avr = true; };
packages."x86_64-linux"."ws2812" =
{ gcc, cflags, avr }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
in mkProgram {
pkgs = pkgs;
gcc = gcc;
cflags = cflags;
pname = "ws2812";
psrc = ./ws2812;
inherit avr;
pbuildInputs = [
(packages."x86_64-linux"."dio" { inherit gcc cflags; })
];
};
devShell."x86_64-linux" =
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
@ -297,9 +319,8 @@
];
in
pkgs.mkShell {
name = "avr-shell";
buildInputs = avr;
name = "Wearables-shell";
buildInputs = avr ++ [ pkgs.gnumake ];
GCC = pkgs.pkgsCross.avr.buildPackages.gcc;
SIMAVR = pkgs.pkgsCross.avr.buildPackages.simavr;
};

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

74
pwm/src/main.c Normal file
View File

@ -0,0 +1,74 @@
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <base.h>
#define FIVE_SECOND_TICK (8000000 * 10) / 256
volatile uint32_t iteration = 0;
void set_up_pwm(void);
void start_pwm(void);
ISR(TIMER0_OVF_vect) {
if (iteration >= FIVE_SECOND_TICK) {
GTCCR |= _BV(TSM);
/*
PORTB &= ~(_BV(2));
PORTB |= _BV(1);
*/
PORTB = _BV(1);
} else {
iteration += 1;
}
}
void set_up_pwm(void) {
// Stop the clock
GTCCR = _BV(TSM) | _BV(PSR0);
TCCR0B = _BV(CS00);
// Set normal mode
TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
// Enable the overflow interrupt
TIMSK = _BV(TOIE0);
}
void start_pwm(void) {
GTCCR &= ~(_BV(TSM));
}
int main (void) {
int8_t step = 1;
uint8_t target = 0;
set_up_pwm();
OCR0A = 0;
PORTB = 0;
DDRB = _BV(2) | _BV(1) | _BV(0);
_delay_ms(50);
PORTB |= _BV(2);
sei();
start_pwm();
while (iteration != FIVE_SECOND_TICK) {
OCR0A = target;
target += step;
if (target == 255) {
step = -1;
} else if (target == 0) {
step = 1;
}
_delay_ms(10);
}
while (1) {}
}

View File

@ -0,0 +1,52 @@
// 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__ ( \
; count out eight bits \
"ldi %z, 8" "\n\t" \
; load the current byte into a temporary register \
"ld __tmp_reg__, %[byte]" "\n\t" \
; shift the temporary register left, saving the msb in SREG (1 cycle) \
"L_%=: " "lsl __tmp_reg__" "\n\t" \
; if SREG is set, branch to I_%= (2 cycles if true, 1 cycle if false) \
"brbs I_%=" "\n\t" \
; SREG was zero, so write a zero to the port \
write_zero(port, bit) \
; Jump to J_%=, the loop cleanup (2 cycles) \
"rjmp J_%=" "\n\t" \
; SREG was one, so write a one to the port \
"I_%=: " write_one(port, bit) \
; Decrement the bits counter (1 cycle) \
"J_%=: " "dec %z" "\n\t" \
; are there any bits left to send? (1 cycle) \
"cpi %z, 0" "\n\t" \
; there are, so go back to L_%= (2 cycles) \
"brne L_%=" "\n\t" \
: /* no outputs */ \
: [byte] "I" (byte) \
)

28
ws2812/main.c Normal file
View File

@ -0,0 +1,28 @@
#include <avr/io.h>
#include <util/delay.h>
#include <dio.h>
#include "ws2812.h"
/*
#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("DDRB"), .what = (void*)&DDRB, },
{ AVR_MCU_VCD_SYMBOL("PORTB"), .what = (void*)&PORTB, },
};
*/
#define PIXEL_COUNT 1
const rgb_t pixels[PIXEL_COUNT] = {
(rgb_t){ .r = 255, .g = 0, .b = 255, },
};
int main (void) {
dio_t pin = (dio_t){ .ddr = &DDRB, .port = &PORTB, .pin = &PINB, .addr = 2 };
dio_set_direction(&pin, LINE_OUT);
np_write_rgb(&pin, pixels, PIXEL_COUNT);
return 0;
}

108
ws2812/ws2812.c Normal file
View File

@ -0,0 +1,108 @@
#include <avr/interrupt.h>
# include "ws2812.h"
// inline void write_zero(dio_t *pin, uint8_t hi, uint8_t low) {
// uint8_t *val;
// uint8_t *hi_reg;
// uint8_t *low_reg;
// __asm__ __volatile__ (
// "ldd %[hi_reg], %[hi]\n"
// "ldd %[low_reg], %[low]\n"
// "ldd %[val], %[pin]\n"
// "or %[val], %[hi_reg]\n"
// "std %[pin], %[val]\n"
// "nop\n"
// "nop\n"
// "and %[val], %[hi_reg]\n"
// "std %[pin], %[val]\n"
// "nop\n"
// "nop\n"
// "nop\n"
// /* Need another 5 cycles */
// : [val] "=r" (val), [hi_reg] "=r" (hi_reg), [low_reg] "=r" (low_reg)
// : [pin] "m" (pin->port), [hi] "m" (hi), [low] "m" (low)
// );
// }
// inline void write_one(dio_t *port, uint8_t hi, uint8_t low) {
// __asm__ __volatile__ (
// "or %0, %1\n"
// "nop\n"
// "nop\n"
// "nop\n"
// "nop\n"
// "nop\n"
// "nop\n"
// "and %0, %1\n"
// /* Need another 5 cycles */
// : /* no outputs */
// : "m" (port->port),
// "m" (hi),
// "m" (low)
// );
// }
inline void write_byte(dio_t *pin, uint8_t hi, uint8_t low, const uint8_t value) {
uint8_t *state_r;
uint8_t *hi_r;
uint8_t *low_r;
uint8_t *counter;
__asm__ __volatile__ (
"ldi r16, 8\n"
"ldi r17, %[value]\n"
"ldd %[hi_r], %[hi]\n"
"ldd %[low_r], %[low]\n"
"ldd %[state_r], %[pin]\n"
"L_%=: " "lsl __tmp_reg__\n"
"brbs I_%=\n"
"or %[state_r], %[hi_r]\n"
"nop\n"
"nop\n"
"and %[state_r], %[low_r]\n"
"nop\n"
"nop\n"
"nop\n"
"rjmp J_%=\n"
"I_%=: "
"brbs I_%=\n"
"or %[state_r], %[hi_r]\n"
"nop\n"
"nop\n"
"nop\n"
"nop\n"
"nop\n"
"and %[state_r], %[low_r]\n"
"J_%=: " "dec %[counter]\n"
"cpi %[counter], 0\n"
"brne L_%="
: [state_r] "=r" (state_r),
[hi_r] "=r" (hi_r),
[low_r] "=r" (low_r),
[counter] "=r" (counter)
: [pin] "m" (pin->port),
[hi] "m" (hi),
[low] "m" (low),
[value] "m" (value)
);
}
void np_write_rgb(dio_t *pin, const rgb_t *pixels, uint8_t length) {
cli();
uint8_t hi = 1 << pin->addr;
uint8_t low = ~hi;
for (int idx = 0; idx < length; idx++) {
write_byte(pin, hi, low, pixels[idx].r);
write_byte(pin, hi, low, pixels[idx].g);
write_byte(pin, hi, low, pixels[idx].b);
}
sei();
}

27
ws2812/ws2812.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __neopixels_h__
#define __neopixels_h__
#include <avr/io.h>
#include <dio.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(dio_t *, const rgb_t *, uint8_t);
// 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