init
This commit is contained in:
commit
e811d778ea
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.o
|
||||
*.elf
|
||||
build/
|
||||
build/*
|
19
analog_read.c
Normal file
19
analog_read.c
Normal file
@ -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;
|
||||
}
|
19
analog_read.h
Normal file
19
analog_read.h
Normal file
@ -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
|
90
main.c
Normal file
90
main.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
26
makefile
Normal file
26
makefile
Normal file
@ -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/*
|
19
menu.h
Normal file
19
menu.h
Normal file
@ -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
|
31
pins.h
Normal file
31
pins.h
Normal file
@ -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
|
89
software_pwm.c
Normal file
89
software_pwm.c
Normal file
@ -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;
|
||||
**/
|
||||
}
|
31
software_pwm.h
Normal file
31
software_pwm.h
Normal file
@ -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
|
96
software_pwm_old.cb
Normal file
96
software_pwm_old.cb
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user