Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 136a1da5b3 | |
Savanni D'Gerinel | 5c28706133 | |
Savanni D'Gerinel | ade8034b0b | |
Savanni D'Gerinel | a16cb1d54e | |
Savanni D'Gerinel | 6c9dbec322 |
27
flake.nix
27
flake.nix
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 -D -U flash:w:$1:i
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
@ -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) \
|
||||
)
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue