From e811d778ea51ac1e25588a9cc957fca4f532ea5a Mon Sep 17 00:00:00 2001 From: Brett Weiland Date: Fri, 27 Oct 2023 07:10:26 -0500 Subject: init --- .gitignore | 4 +++ analog_read.c | 19 +++++++++++ analog_read.h | 19 +++++++++++ bruh | 1 + main.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 26 +++++++++++++++ menu.h | 19 +++++++++++ pins.h | 31 +++++++++++++++++ software_pwm.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ software_pwm.h | 31 +++++++++++++++++ software_pwm_old.cb | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 425 insertions(+) create mode 100644 .gitignore create mode 100644 analog_read.c create mode 100644 analog_read.h create mode 100644 bruh create mode 100644 main.c create mode 100644 makefile create mode 100644 menu.h create mode 100644 pins.h create mode 100644 software_pwm.c create mode 100644 software_pwm.h create mode 100644 software_pwm_old.cb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10dd538 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.elf +build/ +build/* diff --git a/analog_read.c b/analog_read.c new file mode 100644 index 0000000..5ea0b21 --- /dev/null +++ b/analog_read.c @@ -0,0 +1,19 @@ +#include +#include "pins.h" + +void init_adc() { + PRR = 0; + DDRF = 0; + + //enable ADC0 input, refrence is AREF (wire to 5v) + ADMUX = 0b01100000; + + //enable adc, disable interrupts, clear pending reading, slowest speed + ADCSRA = 0b10000111; +} + +uint8_t analog_read_8bit() { + ADCSRA |= (1 << ADSC); + while(ADCSRA & (1 << ADSC)); + return ADCH; +} diff --git a/analog_read.h b/analog_read.h new file mode 100644 index 0000000..ee6ba27 --- /dev/null +++ b/analog_read.h @@ -0,0 +1,19 @@ +#ifndef ANALOG_READ_H +#define ANALOG_READ_H + +/** analog read: + * change ADMUX MUXn and REFS1:0 to select channel, + * ADC enbale bit: ADEN in ADCSRA + * ADC is data reg. Right ajusted, can be left by ADLAR in ADMUX + * If left ajusted & no more then 8 bits, read ADCH, otherwise first ADCL + * Conversion: + * Set ADSC (is cleared once conversion is done) + * Or: ADTS mux bits in ADCSRA for auto trigger (will trigger on high) + * interrupt flag will be enabled +**/ + +//PORTF pin 1 hardcoded as analog +uint8_t analog_read_8bit(); +void init_adc(); + +#endif diff --git a/bruh b/bruh new file mode 100644 index 0000000..64845fb --- /dev/null +++ b/bruh @@ -0,0 +1 @@ +` \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..44bbb2d --- /dev/null +++ b/main.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +#include "pins.h" +#include "menu.h" +#include "software_pwm.h" +#include "analog_read.h" + +/** light projector controller. How it'll work: + * Potentiometer with a voltage dividor to send 0-5v into analog input. + * Button [mode] to change modes. + * Button [select] to select values within mode. + * + * [modes] (press mode button to switch, select button to select children options to ajust with potentiometer): + * RGB shift + * rgb hue speed + * + * Just white + * + * Control RGBW + * R + * G + * B + * W + * + * Chagne motor speed + * motor speed + * + * OFF + * + * + **/ + + + + +void test_adc() { + DDRA = 0xff; + for(;;) { + + int shift_amount = (8 * ((float)analog_read_8bit() / UINT8_MAX)); + + if(shift_amount < 0) { + PORTA = 0; + continue; + } + else if(shift_amount >= 8) { + PORTA = 0xff; + continue; + } + + PORTA = (1 << shift_amount); + } +} + +void test_softpwm() { + /** + softpwm_set(0, 0); + softpwm_set(1, 31); + softpwm_set(2, 31 * 2); + softpwm_set(3, 31 * 3); + softpwm_set(4, 31 * 4); + softpwm_set(5, 31 * 5); + softpwm_set(6, 31 * 6); + softpwm_set(7, 255); + **/ + for(;;); +} + +int main() { + //init_adc(); + //test_adc(); + init_softpwm(); + test_softpwm(); + + int mode = 0; + //int submode = 0; + switch(mode) { + case RAINBOW: + break; + case WHITE: + break; + case RGBW_MANUAL: + break; + case MOTOR_SPEED: + break; + } +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..2cd372d --- /dev/null +++ b/makefile @@ -0,0 +1,26 @@ +TOOLCHAIN_DIR=/home/indigo/packs/avr8-gnu-toolchain-linux_x86_64 +CC=$(TOOLCHAIN_DIR)/bin/avr-gcc +INC=$(TOOLCHAIN_DIR)/avr/include +OUT=payload.elf +SRCFILES := $(wildcard *.c) +OBJFILES := $(patsubst %.c,build/%.o,$(SRCFILES)) + +DEVICE=atmega165pa +F_CPU=8000000 #uses internal 8mhz RC clock +#F_CPU=10000000 #uses external 10mhz crystal to ensure accuracy. 10mhz is weird but I just had this one laying around + +CCOPTS := -Wall -Wextra -I $(INC) -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) -O1 + + +make: $(OBJFILES) + $(CC) $(CCOPTS) -o $(OUT) $(OBJFILES) + +build/%.o: %.c + if [ ! -d "build" ]; then mkdir -p build; fi + $(CC) $(CCOPTS) -c -o $@ $< + +install: $(OUT) + doas avrdude -v -c usbtiny -p atmega165pa -U flash:w:payload.elf:e + +clean: + rm -f *.o *.elf build/* diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..5dd4022 --- /dev/null +++ b/menu.h @@ -0,0 +1,19 @@ +#ifndef MENU_H +#define MENU_H + +enum MODES { + RAINBOW, + WHITE, + RGBW_MANUAL, + MOTOR_SPEED, + OFF +}; + +enum RGBW_MANUAL_OPTS { + RGBW_CONF_R, + RGBW_CONF_G, + RGBW_CONF_B, + RGBW_CONF_W +}; + +#endif diff --git a/pins.h b/pins.h new file mode 100644 index 0000000..8206fda --- /dev/null +++ b/pins.h @@ -0,0 +1,31 @@ +#ifndef PINS_H +#define PINS_H + +//TODO maybe clarify pin names, lazy rn + +//digital input register includes our buttons +#define DIGITAL_INPUT_DDR DDRA +#define DIGITAL_INPUT_PORT PORTA + +#define MODE_BUTTON_P PORTA0 +#define SELECT_BUTTON_P PORTA1 + +//analog input we'll dedicate to analog inputs (only the single potentiometer) +#define ADC_DDR DDRF + +//all of this register will be used as our 8 pwm outputs +#define SOFTPWM_DDR DDRC +#define SOFTPWM_PORT PORTC + +#define LED_W_1 PORTC0 +#define LED_W_2 PORTC1 + +#define LED_R_1 PORTC2 +#define LED_G_1 PORTC3 +#define LED_B_1 PORTC4 + +#define LED_R_2 PORTC5 +#define LED_G_2 PORTC6 +#define LED_B_2 PORTC7 + +#endif diff --git a/software_pwm.c b/software_pwm.c new file mode 100644 index 0000000..561edfd --- /dev/null +++ b/software_pwm.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +#include "software_pwm.h" +#include "pins.h" + + +static struct link_pair { + struct pwm_event *next; + struct pwm_event *prev; +}; + +//if next is ever zero, node is assumed not part of list/disabled +static struct pwm_event { + struct link_pair active; + struct link_pair twins; + uint8_t time; + uint8_t pins; + bool enabled; +} events[SOFTPWM_PIN_COUNT + 1]; + + +#define LIST_INSERT(new, former, member) (\ + list_insert(&(new.member), &(former.member), offsetof(struct pwm_event, time))) + +static void list_insert(struct link_pair *new_links, struct link_pair *former_links, size_t parent_offset) { + +} + +static void list_remove(struct link_pair *event) { +} + +void init_softpwm() { + + //this one never moves + events[SOFTPWM_PIN_COUNT].time = 0; + events[SOFTPWM_PIN_COUNT].pins = 0; + events[SOFTPWM_PIN_COUNT].active.next = &events[SOFTPWM_PIN_COUNT]; + events[SOFTPWM_PIN_COUNT].active.prev = &events[SOFTPWM_PIN_COUNT]; + events[SOFTPWM_PIN_COUNT].enabled = true; + + //TODO: move all to zero once init_softpwm tested + for(size_t event_i = 0; event_i < SOFTPWM_PIN_COUNT; event_i++) { + events[event_i].time = event_i; + events[event_i].pins = (1 << (event_i + 1)) - 1; + LIST_INSERT(events[event_i], events[((int)event_i - 1) % (SOFTPWM_PIN_COUNT + 1)], active); + } + + SOFTPWM_DDR = 0xff; + + //use external 10mhz clock (we just have one laying around) + //then prescale by 1024. Check at different freqs for brightness + TCCR0A = 0b00000010; + TIMSK0 = 0b00000011; + OCR0A = events[SOFTPWM_PIN_COUNT].time; + TIFR0 = 0b00000010; //interupts enabled! + sei(); +} + +void softpwm_set(uint8_t pin, uint8_t duty) { + /** + struct pwm_event *event_unmoved = &events[pin]; //TODO describe if you want ig + struct pwm_event *this_event = &events[pin]; + this_event->time = duty; + if(duty < events[pin].time) { + while(duty < event_unmoved->time) event_unmoved = event_unmoved->next; + } + else { + } + **/ +} + +/** +ISR(TIMER0_OVF_vect) { +} +**/ + +ISR(TIMER0_COMP_vect) { + /** + static struct pwm_event *on_event = &events[SOFTPWM_PIN_COUNT]; + SOFTPWM_PORT = on_event->pins; + on_event = on_event->next; + OCR0A = on_event->time; + **/ +} diff --git a/software_pwm.h b/software_pwm.h new file mode 100644 index 0000000..1976c65 --- /dev/null +++ b/software_pwm.h @@ -0,0 +1,31 @@ +#ifndef SOFTWARE_PWM_H +#define SOFTWARE_PWM_H + +#include + +#define SOFTPWM_PIN_COUNT 8 +#define PWM_FREQ 300 //hz + +/** Our microcontroller we have laying around only has 4 hardware pwm channels. + * Because we want 8, we will create software PWM as an alternative. + * + * Notes to self to forcast nessessary optimizations (translates to color accuracy): + * Our error margin is 1/255th (13us at pwm freq 300hz) of a cycle. + * Cycles until next channel could be triggered: + * 209 at 16Mhz + * 130 at 10Mhz (have one available) + * 104 at 8Mhz + * 13 at 1Mhz (have one available) + * + * To decrease the amount of cycles every pwm timer interrupt, we'll generate the + * + * We might also want to make sure these values aren't updated every loop + * from random noise coming from the potentiometer. Test without, decide then. + */ + +void init_softpwm(); +void initilize_duty_cycle(uint8_t pin); +void softpwm_set(uint8_t pin, uint8_t duty); + + +#endif diff --git a/software_pwm_old.cb b/software_pwm_old.cb new file mode 100644 index 0000000..db609af --- /dev/null +++ b/software_pwm_old.cb @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +#include "software_pwm.h" +#include "pins.h" + +static struct pwm_event { + //ENTIRE port + //(ex if pin 3, 2, 4 are on and we want 6, this should be 0b01011100) + uint8_t port; + uint8_t time; + struct pwm_event **map_ptr; +} events[SOFTPWM_PIN_COUNT]; + +static struct pwm_event *pin_event_map[SOFTPWM_PIN_COUNT]; + + +void init_softpwm() { + SOFTPWM_DDR = 0xff; + for(size_t event_i = 0; event_i < SOFTPWM_PIN_COUNT; event_i++) { + pin_event_map[event_i] = &events[event_i]; + events[event_i].map_ptr = &pin_event_map[event_i]; + events[event_i].time = event_i + 1; + events[event_i].port = (1 << (event_i + 1)) - 1; + } + + //use external 10mhz clock (we just have one laying around) + //then prescale by 1024. Check at different freqs for brightness + TCCR0A = 0b00000010; + TIMSK0 = 0b00000011; + OCR0A = events[0].time; + TIFR0 = 0b00000010; //interupts enabled! + sei(); +} + + +static void event_move(struct pwm_event *dest, struct pwm_event *src) { + *dest = *src; + *(dest->map_ptr) = src; +} + +/** + * okay, let me explain. This is really weird and gross, I know. + * I did it when I was *very* low on sleep. + * Why did I do it like this? Well, I was worried about accuracy; + * for some stupid reason I thought I really needed the actual pwm ISR to be as fast + * as possible. I don't feel like redoing this in a simpler fasion, + * so while this code *is* embaracing, I'll get around to it later. + **/ +void softpwm_set(uint8_t pin, uint8_t value) { + struct pwm_event *event_to_shift; + struct pwm_event modified_event_cpy = *pin_event_map[pin]; + + modified_event_cpy.time = value; + if(value < modified_event_cpy.time) { + for(event_to_shift = pin_event_map[pin - 1]; + (event_to_shift >= events) + || (modified_event_cpy.time < event_to_shift[-1].time); + event_to_shift = &event_to_shift[-1]) + { + event_to_shift->port |= (1 << pin); + event_move(&event_to_shift[1], event_to_shift); + } + event_move(&event_to_shift[1], &modified_event_cpy); + return; + } + if(value > modified_event_cpy.time) { + for(event_to_shift = pin_event_map[pin + 1]; + (event_to_shift < &events[SOFTPWM_PIN_COUNT]) + || (modified_event_cpy.time > event_to_shift[1].time); + event_to_shift = &event_to_shift[1]) + { + event_to_shift->port &= ~(1 << pin); + event_move(&event_to_shift[-1], event_to_shift); + } + } + event_move(&event_to_shift[-1], &modified_event_cpy); +} + +ISR(TIMER0_OVF_vect) { + SOFTPWM_PORT = 0; + PORTA ^= 1; +} + +//ISR for pwm timer. Need to look at assembly output to ensure proper optimization +//(would it be faster to have two different arrays vs a struct? idk if the offset is computed compile or runtime) +ISR(TIMER0_COMP_vect) { + static uint8_t eventcount = 0; + //eventcount = (eventcount <= SOFTPWM_PIN_COUNT) ? eventcount + 1 : 0; + SOFTPWM_PORT = events[eventcount].port; + eventcount = (eventcount + 1) % 8; + OCR0A = events[eventcount].time; + PORTA ^= 2; +} -- cgit v1.2.3