summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--analog_read.c19
-rw-r--r--analog_read.h19
-rw-r--r--bruh1
-rw-r--r--main.c90
-rw-r--r--makefile26
-rw-r--r--menu.h19
-rw-r--r--pins.h31
-rw-r--r--software_pwm.c89
-rw-r--r--software_pwm.h31
-rw-r--r--software_pwm_old.cb96
11 files changed, 425 insertions, 0 deletions
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 <avr/io.h>
+#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 <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+
+#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 <stdint.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#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 <stdint.h>
+
+#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 <stdint.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#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;
+}