From 599f5a32d9f40bcf4ae3ea28b8c03192757dc18a Mon Sep 17 00:00:00 2001 From: jakegoodwin Date: Thu, 2 Mar 2023 21:16:03 -0800 Subject: [PATCH] finished code and testing of functions on AVR 328P. --- AVR_ENCODER.hex | 22 +++++ Makefile | 217 +++++++++++++++++++++++++++++++++++++++++++ README.md | 46 ++++++++- src/rotary_encoder.c | 177 +++++++++++++++++++++++++++++++++++ 4 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 AVR_ENCODER.hex create mode 100644 Makefile create mode 100644 src/rotary_encoder.c diff --git a/AVR_ENCODER.hex b/AVR_ENCODER.hex new file mode 100644 index 0000000..5383e15 --- /dev/null +++ b/AVR_ENCODER.hex @@ -0,0 +1,22 @@ +:100000000C9434000C9446000C9446000C9446006A +:100010000C9446000C9446000C9446000C94460048 +:100020000C9446000C9446000C9446000C94460038 +:100030000C9446000C9446000C9446000C94460028 +:100040000C9446000C9446000C9446000C94460018 +:100050000C9446000C9446000C9446000C94460008 +:100060000C9446000C94460011241FBECFEFD8E03C +:10007000DEBFCDBF21E0A0E0B1E001C01D92A23003 +:10008000B207E1F70E9491000C949F000C940000CD +:10009000389A90E031E09817D1F028B1232728B999 +:1000A0002FEF4FE756E1215040405040E1F700C0AC +:1000B000000028B1232728B92FEF4FE756E1215040 +:1000C00040405040E1F700C000009F5FE4CF08953A +:1000D00087B1897F87B988B1897F88B9089586B14A +:1000E000867090E09595879520910101281749F039 +:1000F0008093010190910001990F990F892B8093B2 +:100100000001809100018B3431F0873841F4109266 +:10011000000182E008951092000181E0089580E0DE +:1001200008950E94680082E00E9448000E946F00CB +:10013000813019F08230B9F3F9CF81E0F5CFF8942E +:02014000FFCFEF +:00000001FF diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2a07c66 --- /dev/null +++ b/Makefile @@ -0,0 +1,217 @@ + +##########------------------------------------------------------########## +########## Project-specific Details ########## +########## Check these every time you start a new project ########## +##########------------------------------------------------------########## + +#MCU = atmega168p +#MCU = atmega2560 +#MCU = attiny85 +MCU = atmega328p +F_CPU = 14745600UL +#F_CPU = 1000000UL +BAUD = 9600UL +## Also try BAUD = 19200 or 38400 if you're feeling lucky. + +## A directory for common include files and the simple USART library. +## If you move either the current folder or the Library folder, you'll +## need to change this path to match. +#LIBDIR = ../../AVR-Programming-Library +#LIBDIR = /usr/lib/gcc/avr/5.4.0/ +LIBDIR = /lib/avr/ +SUB_MODULES = ./lib/*/src/ + +##########------------------------------------------------------########## +########## Programmer Defaults ########## +########## Set up once, then forget about it ########## +########## (Can override. See bottom of file.) ########## +##########------------------------------------------------------########## + +PROGRAMMER_TYPE = usbasp-clone +# extra arguments to avrdude: baud rate, chip type, -F flag, etc. +PROGRAMMER_ARGS = -b 9600 -B 4 -v + +##########------------------------------------------------------########## +########## Program Locations ########## +########## Won't need to change if they're in your PATH ########## +##########------------------------------------------------------########## + +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +AVRSIZE = avr-size +AVRDUDE = avrdude + +##########------------------------------------------------------########## +########## Makefile Magic! ########## +########## Summary: ########## +########## We want a .hex file ########## +########## Compile source files into .elf ########## +########## Convert .elf file into .hex ########## +########## You shouldn't need to edit below. ########## +##########------------------------------------------------------########## + +## The name of your project (without the .c) +# TARGET = blinkLED +## Or name it automatically after the enclosing directory +TARGET = $(lastword $(subst /, ,$(CURDIR))) + +# Object files: will find all .c/.h files in current directory +# and in LIBDIR. If you have any other (sub-)directories with code, +# you can add them in to SOURCES below in the wildcard statement. +SOURCES=$(wildcard ./src/*.c $(LIBDIR)/*.c $(SUB_MODULES)/*.c) +OBJECTS=$(SOURCES:.c=.o) +HEADERS=$(SOURCES:.c=.h) + +## Compilation options, type man avr-gcc if you're curious. +CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I. -I$(LIBDIR) +CFLAGS = -Os -g -std=gnu99 -Wall +## Use short (8-bit) data types +CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +## Splits up object files per function +CFLAGS += -ffunction-sections -fdata-sections +LDFLAGS = -Wl,-Map,$(TARGET).map +## Optional, but often ends up with smaller code +LDFLAGS += -Wl,--gc-sections +## Relax shrinks code even more, but makes disassembly messy +## LDFLAGS += -Wl,--relax +## LDFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf +## LDFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf +TARGET_ARCH = -mmcu=$(MCU) + +## Explicit pattern rules: +## To make .o files from .c files +%.o: %.c $(HEADERS) Makefile + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<; + +$(TARGET).elf: $(OBJECTS) + $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@ + +%.hex: %.elf + $(OBJCOPY) -j .text -j .data -O ihex $< $@ + +%.eeprom: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +## These targets don't have files named after them +.PHONY: all disassemble disasm eeprom size clean squeaky_clean flash fuses + +all: $(TARGET).hex + +debug: + @echo + @echo "Source files:" $(SOURCES) + @echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD) + @echo + +# Optionally create listing file from .elf +# This creates approximate assembly-language equivalent of your code. +# Useful for debugging time-sensitive bits, +# or making sure the compiler does what you want. +disassemble: $(TARGET).lst + +disasm: disassemble + +# Optionally show how big the resulting program is +size: $(TARGET).elf + $(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf + +clean: + rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \ + $(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \ + $(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \ + $(TARGET).eeprom + +squeaky_clean: + rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ *.eeprom + +##########------------------------------------------------------########## +########## Programmer-specific details ########## +########## Flashing code to AVR using avrdude ########## +##########------------------------------------------------------########## + +flash: $(TARGET).hex + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$< + +## An alias +program: flash + +flash_eeprom: $(TARGET).eeprom + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$< + +avrdude_terminal: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt + +## If you've got multiple programmers that you use, +## you can define them here so that it's easy to switch. +## To invoke, use something like `make flash_arduinoISP` +flash_usbtiny: PROGRAMMER_TYPE = usbtiny +flash_usbtiny: PROGRAMMER_ARGS = # USBTiny works with no further arguments +flash_usbtiny: flash + +flash_usbasp: PROGRAMMER_TYPE = usbasp +flash_usbasp: PROGRAMMER_ARGS = # USBasp works with no further arguments +flash_usbasp: flash + +flash_arduinoISP: PROGRAMMER_TYPE = avrisp +flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0 +## (for windows) flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P com5 +flash_arduinoISP: flash + +flash_109: PROGRAMMER_TYPE = avr109 +flash_109: PROGRAMMER_ARGS = -b 9600 -P /dev/ttyUSB0 +flash_109: flash + +##########------------------------------------------------------########## +########## Fuse settings and suitable defaults ########## +##########------------------------------------------------------########## + +## Mega 48, 88, 168, 328 default values +#LFUSE=0x62 ##Low Fuse +#HFUSE=0xdf ##High fuses +#EFUSE=0x00 ## + +## Mega 328p fuses +LFUSE = 0x62 +HFUSE = 0xd9 +EFUSE = 0xff + + +## Generic +FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m + +fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \ + $(PROGRAMMER_ARGS) $(FUSE_STRING) +show_fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv + +## Called with no extra definitions, sets to defaults +set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m +set_default_fuses: fuses + +## For external clock low-power crystal. +# The low order fuse is set for a low-power crystal. 14.7456Mhz +# cksel[3:1] 8.0-16.0 = 111 //Sets it for the range of 8Mhz - 16Mhz +# cksel[0] 1 SUT[1:0] 11 //gives 16K ck startup time aprox 65ms +set_crystal_fuses: LFUSE = 0xFF +set_crystal_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m +set_crystal_fuses: fuses + +## Set the fuse byte for full-speed mode +## Note: can also be set in firmware for modern chips +set_fast_fuse: LFUSE = 0xE2 +set_fast_fuse: FUSE_STRING = -U lfuse:w:$(LFUSE):m +set_fast_fuse: fuses + +## Set the EESAVE fuse byte to preserve EEPROM across flashes +set_eeprom_save_fuse: HFUSE = 0xD7 +set_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m +set_eeprom_save_fuse: fuses + +## Clear the EESAVE fuse byte +clear_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m +clear_eeprom_save_fuse: fuses diff --git a/README.md b/README.md index c8b47a9..4c08922 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,47 @@ # AVR_ENCODER -This is small library for rotary encoders often found being used for controls or volume knobs in embedded platforms. +This is small library for rotary encoders often found being used for controls +or volume knobs in embedded platforms. + + +The recomended way to use the code is by calling the poll_encoder() function +in the C file. + +This allows the use of polling for monitoring the state of the encoder. This +should be fine for most applications that don't require interrupts for encoder +use. + +The only issue would be if running the AVR at very very low speed and +preforming too many other taskts to the point that the polling loop doesn't +capture the full wave form from the encoder. But this is a well known weakness +in polling. + + +## Demo Funciton: +```C +//This is just an example function of how you could use it. +int demo() { + init_encoder(); + uint8_t volume = 0; + led_blink(2); + + while(1) { + switch(poll_encoder()) { + case CW: + if(volume < 100){volume++;} + led_blink(1); + break; + case CCW: + if(volume > 0){volume--;} + led_blink(2); + break; + default: + ;//nothing. + } + + //Do other polling operations here + } + return 0; +} +``` + diff --git a/src/rotary_encoder.c b/src/rotary_encoder.c new file mode 100644 index 0000000..05f16c0 --- /dev/null +++ b/src/rotary_encoder.c @@ -0,0 +1,177 @@ +/* + * Author: Jake Goodwin + * Date: 2023 + * Description: Small library for using standard rotary encoders. + */ + +/* + * Rotary pin state bits + * | NOUSE |A0 |B0 |A1 |B1 | + * | 0000b | 0 | 0 | 0 | 0 | + */ + +/* Rotary encoder mechanical contacts. + * Clockwise sequence: + * | A B | DEC + * t1 | 0 1 | 0x1 + * t2 | 0 0 | 0x0 + * t3 | 1 0 | 0x2 + * t4 | 1 1 | 0x3 + * + * Counter-Clockwise sequence: + * | A B | Hex + * t1 | 1 0 | 0x2 + * t2 | 0 0 | 0x0 + * t3 | 0 1 | 0x1 + * t4 | 1 1 | 0x3 + * + * If tossed into a 8bit register or var: + * CW: 0b01001011 = 75 + * CCW: 0b10000111 = 135 + */ + + +#include +#include +#include + +// PIN DEFINITIONS +//PUT YOUR PINS IN FOR ENCODER A/B +//Along with pin offset if needed. So i'm using pins 1,2 so because I +//skipped 0 I set the offset too 1. + +#define ENCODER_A PINC1 +#define ENCODER_B PINC2 +#define PIN_OFFSET 1 +#define ENCODER_DDR DDRC +#define ENCODER_PORT PORTC + +#define INPUT_REG PINC + +#define LED_PIN PINC0 + +//############################# +//Globals +//############################# + +//if you want you can replace this with just encoder_byte +//and bitshift/mask to compare it with the pins state, but it's less readable. +uint8_t old_state = 0x0; +uint8_t encoder_byte = 0x0; + +//A more friendly way to check state. +enum encoder_dir { + CW = 1, + CCW = 2, + none = 0, +}; + + +//############################# +//FUNCTIONS +//############################# +/* + * Input: None + * Output: None + * Description: Toggles the pin for the LED indicator. + */ +void led_blink(uint8_t x) { + //Set the DDR for output. + DDRC |= (1<> PIN_OFFSET); + + //we want to check for the correct sequence of bits. + //we also only want to accept new states as input. Not old states. + if(encoder_state != old_state){ + old_state = encoder_state; + encoder_byte = (encoder_byte<<2); + encoder_byte |= encoder_state; + } + + switch(encoder_byte) { + case CCW: + encoder_byte = 0x0; + encoder_state= 2; + break; + case CW: + encoder_byte = 0x0; + encoder_state= 1; + break; + default: + encoder_state= 0x0; + break; + } + + return encoder_state; + +} + + + + +//This is just an example function of how you could use it. +int demo() { + init_encoder(); + uint8_t volume = 0; + led_blink(2); + + while(1) { + switch(poll_encoder()) { + case CW: + if(volume < 100){volume++;} + led_blink(1); + break; + case CCW: + if(volume > 0){volume--;} + led_blink(2); + break; + default: + ;//nothing. + } + + //Do other polling operations here + } + return 0; +} + +