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
	 Brett Weiland
						Brett Weiland