├── edit.sh ├── LICENSE ├── USART.h ├── README.md ├── main.c ├── USART.c └── Makefile /edit.sh: -------------------------------------------------------------------------------- 1 | ctags * /usr/avr/include/avr/iomx8.h /usr/avr/include/avr/interrupt.h /usr/avr/include/avr/eeprom.h /usr/avr/include/avr/pgmspace.h /usr/avr/include/avr/sleep.h /usr/avr/include/avr/wdt.h /usr/avr/include/avr/power.h /usr/avr/include/util/delay.h /usr/avr/include/util/atomic.h 2 | vim *.c *.h Makefile 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Elliot Williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /USART.h: -------------------------------------------------------------------------------- 1 | /* Functions to initialize, send, receive over USART 2 | 3 | initUSART requires BAUD to be defined in order to calculate 4 | the bit-rate multiplier. 5 | */ 6 | 7 | #ifndef BAUD /* if not defined in Makefile... */ 8 | #define BAUD 9600 /* set a safe default baud rate */ 9 | #endif 10 | 11 | /* These are defined for convenience */ 12 | #define USART_HAS_DATA bit_is_set(UCSR0A, RXC0) 13 | #define USART_READY bit_is_set(UCSR0A, UDRE0) 14 | 15 | /* Takes the defined BAUD and F_CPU, 16 | calculates the bit-clock multiplier, 17 | and configures the hardware USART */ 18 | void initUSART(void); 19 | 20 | /* Blocking transmit and receive functions. 21 | When you call receiveByte() your program will hang until 22 | data comes through. We'll improve on this later. */ 23 | void transmitByte(uint8_t data); 24 | uint8_t receiveByte(void); 25 | 26 | void printString(const char myString[]); 27 | /* Utility function to transmit an entire string from RAM */ 28 | void readString(char myString[], uint8_t maxLength); 29 | /* Define a string variable, pass it to this function 30 | The string will contain whatever you typed over serial */ 31 | 32 | void printByte(uint8_t byte); 33 | /* Prints a byte out as its 3-digit ascii equivalent */ 34 | void printWord(uint16_t word); 35 | /* Prints a word (16-bits) out as its 5-digit ascii equivalent */ 36 | 37 | void printBinaryByte(uint8_t byte); 38 | /* Prints a byte out in 1s and 0s */ 39 | char nibbleToHex(uint8_t nibble); 40 | void printHexByte(uint8_t byte); 41 | /* Prints a byte out in hexadecimal */ 42 | uint8_t getNumber(void); 43 | /* takes in up to three ascii digits, 44 | converts them to a byte when press enter */ 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # embed_with_elliot-circular_buffer 2 | 3 | Demo code to go along with [Hackaday article on Circular Buffers](http://wp.me/pk3lN-Jvq). 4 | 5 | ## Try Me! 6 | 7 | Straight out of the box, this will compile for AVR on any platform that's got `AVR-GCC` and `make` installed. The code will work with AVR Studio or even Arduino, because it's just C. However you're compiling code, just do that. 8 | 9 | ### For Arduino 10 | 11 | To compile / flash C code on Arduino: 12 | 13 | 1. Create a new sketch, delete everything (the setup and loop functions) from the default `.ino` file, and quit Arduino 14 | 2. Copy the `.c` and `.h` files over to the sketch's directory 15 | 3. Open the Arduino IDE again and you'll see the code opened up for you in tabs 16 | 4. Compile as usual. Edit. Flash. Play around. 17 | 18 | ## Versions 19 | 20 | There are multiple demo versions hidden in the history of this project. Check them out! 21 | 22 | * [Naive Demo (a793508)](https://github.com/hexagon5un/embed_with_elliot-circular_buffer/commit/a793508bc38481f820146f62dcdacfbeef1df6ee): with lots of printing. Good to watch it run and figure out what's going on. Probably too long, sorry. 23 | 24 | * [Loopback Demo (f33be28)](https://github.com/hexagon5un/embed_with_elliot-circular_buffer/commit/f33be289cb8738c8aa881feaf8aeadc783984841): shows how two buffers (one for receiving and one for transmitting) and some interrupt code can handle serial input and output. This is a lot like what the Arduino libraries do under the hood, but written less generally and much more transparently, IMO. 25 | 26 | * [Words Demo (55ab726)](https://github.com/hexagon5un/embed_with_elliot-circular_buffer/commit/55ab72683633151d47e069111cc883e0645cd7e3): Uses interrupt-driven RX and TX and the `bufferPeek()` function to print out what you type in only after a complete word has been sent. 27 | 28 | 29 | 30 | ## Serial Terminals 31 | 32 | Most of the examples depend on having an instantaneous-response serial terminal program. Unfortunately, you Arduino folks are out in the cold here -- Arduino's default serial monitor program only responds when you hit the "Enter" key. 33 | 34 | I use a python module ([PySerial](https://wiki.python.org/moin/PySerial)) that just happens to have a nice built-in serial terminal emulator program. This will work anywhere you've got Python installed: `python -m serial.tools.miniterm --cr $PORT $BAUD` 35 | 36 | On Windows, try [Realterm](http://realterm.sourceforge.net/) or [Tera Term](http://ttssh2.osdn.jp/) if you're not running XP anymore. (R.I.P. Hyperterminal.) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "USART.h" 4 | 5 | #define BUFFER_SIZE 16 6 | 7 | enum BufferStatus {BUFFER_OK, BUFFER_EMPTY, BUFFER_FULL}; 8 | 9 | struct Buffer { 10 | uint8_t data[BUFFER_SIZE]; 11 | uint8_t newest_index; 12 | uint8_t oldest_index; 13 | }; 14 | struct Buffer buffer = {{}, 0, 0}; 15 | 16 | enum BufferStatus bufferWrite(uint8_t byte){ 17 | uint8_t next_index = (buffer.newest_index+1) % BUFFER_SIZE; 18 | 19 | if (next_index == buffer.oldest_index){ 20 | return BUFFER_FULL; 21 | } 22 | buffer.data[buffer.newest_index] = byte; 23 | buffer.newest_index = next_index; 24 | return BUFFER_OK; 25 | } 26 | 27 | enum BufferStatus bufferRead(uint8_t *byte){ 28 | if (buffer.newest_index == buffer.oldest_index){ 29 | return BUFFER_EMPTY; 30 | } 31 | *byte = buffer.data[buffer.oldest_index]; 32 | buffer.oldest_index = (buffer.oldest_index+1) % BUFFER_SIZE; 33 | return BUFFER_OK; 34 | } 35 | 36 | void dumpBuffer(void){ 37 | 38 | for (uint8_t i=0; i 20 | #include "USART.h" 21 | #include 22 | 23 | void initUSART(void) { /* requires BAUD */ 24 | UBRR0H = UBRRH_VALUE; /* defined in setbaud.h */ 25 | UBRR0L = UBRRL_VALUE; 26 | #if USE_2X 27 | UCSR0A |= (1 << U2X0); 28 | #else 29 | UCSR0A &= ~(1 << U2X0); 30 | #endif 31 | /* Enable USART transmitter/receiver */ 32 | UCSR0B = (1 << TXEN0) | (1 << RXEN0); 33 | UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); /* 8 data bits, 1 stop bit */ 34 | } 35 | 36 | 37 | void transmitByte(uint8_t data) { 38 | /* Wait for empty transmit buffer */ 39 | loop_until_bit_is_set(UCSR0A, UDRE0); 40 | UDR0 = data; /* send data */ 41 | } 42 | 43 | uint8_t receiveByte(void) { 44 | loop_until_bit_is_set(UCSR0A, RXC0); /* Wait for incoming data */ 45 | return UDR0; /* return register value */ 46 | } 47 | 48 | 49 | /* Here are a bunch of useful printing commands */ 50 | 51 | void printString(const char myString[]) { 52 | uint8_t i = 0; 53 | while (myString[i]) { 54 | transmitByte(myString[i]); 55 | i++; 56 | } 57 | } 58 | 59 | void readString(char myString[], uint8_t maxLength) { 60 | char response; 61 | uint8_t i; 62 | i = 0; 63 | while (i < (maxLength - 1)) { /* prevent over-runs */ 64 | response = receiveByte(); 65 | transmitByte(response); /* echo */ 66 | if (response == '\r') { /* enter marks the end */ 67 | break; 68 | } 69 | else { 70 | myString[i] = response; /* add in a letter */ 71 | i++; 72 | } 73 | } 74 | myString[i] = 0; /* terminal NULL character */ 75 | } 76 | 77 | void printByte(uint8_t byte) { 78 | /* Converts a byte to a string of decimal text, sends it */ 79 | transmitByte('0' + (byte / 100)); /* Hundreds */ 80 | transmitByte('0' + ((byte / 10) % 10)); /* Tens */ 81 | transmitByte('0' + (byte % 10)); /* Ones */ 82 | } 83 | 84 | void printWord(uint16_t word) { 85 | transmitByte('0' + (word / 10000)); /* Ten-thousands */ 86 | transmitByte('0' + ((word / 1000) % 10)); /* Thousands */ 87 | transmitByte('0' + ((word / 100) % 10)); /* Hundreds */ 88 | transmitByte('0' + ((word / 10) % 10)); /* Tens */ 89 | transmitByte('0' + (word % 10)); /* Ones */ 90 | } 91 | 92 | void printBinaryByte(uint8_t byte) { 93 | /* Prints out a byte as a series of 1's and 0's */ 94 | uint8_t bit; 95 | for (bit = 7; bit < 255; bit--) { 96 | if (bit_is_set(byte, bit)) 97 | transmitByte('1'); 98 | else 99 | transmitByte('0'); 100 | } 101 | } 102 | 103 | char nibbleToHexCharacter(uint8_t nibble) { 104 | /* Converts 4 bits into hexadecimal */ 105 | if (nibble < 10) { 106 | return ('0' + nibble); 107 | } 108 | else { 109 | return ('A' + nibble - 10); 110 | } 111 | } 112 | 113 | void printHexByte(uint8_t byte) { 114 | /* Prints a byte as its hexadecimal equivalent */ 115 | uint8_t nibble; 116 | nibble = (byte & 0b11110000) >> 4; 117 | transmitByte(nibbleToHexCharacter(nibble)); 118 | nibble = byte & 0b00001111; 119 | transmitByte(nibbleToHexCharacter(nibble)); 120 | } 121 | 122 | uint8_t getNumber(void) { 123 | // Gets a numerical 0-255 from the serial port. 124 | // Converts from string to number. 125 | char hundreds = '0'; 126 | char tens = '0'; 127 | char ones = '0'; 128 | char thisChar = '0'; 129 | do { /* shift over */ 130 | hundreds = tens; 131 | tens = ones; 132 | ones = thisChar; 133 | thisChar = receiveByte(); /* get a new character */ 134 | transmitByte(thisChar); /* echo */ 135 | } while (thisChar != '\r'); /* until type return */ 136 | return (100 * (hundreds - '0') + 10 * (tens - '0') + ones - '0'); 137 | } 138 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ##########------------------------------------------------------########## 3 | ########## Project-specific Details ########## 4 | ########## Check these every time you start a new project ########## 5 | ##########------------------------------------------------------########## 6 | 7 | #MCU = atmega168p 8 | MCU = atmega48p 9 | F_CPU = 8000000UL 10 | BAUD = 9600UL 11 | ## Also try BAUD = 19200 or 38400 if you're feeling lucky. 12 | 13 | ## A directory for common include files and the simple USART library. 14 | ## If you move either the current folder or the Library folder, you'll 15 | ## need to change this path to match. 16 | LIBDIR = . 17 | 18 | ##########------------------------------------------------------########## 19 | ########## Programmer Defaults ########## 20 | ########## Set up once, then forget about it ########## 21 | ########## (Can override. See bottom of file.) ########## 22 | ##########------------------------------------------------------########## 23 | 24 | PROGRAMMER_TYPE = usbtiny 25 | # extra arguments to avrdude: baud rate, chip type, -F flag, etc. 26 | PROGRAMMER_ARGS = 27 | 28 | ##########------------------------------------------------------########## 29 | ########## Program Locations ########## 30 | ########## Won't need to change if they're in your PATH ########## 31 | ##########------------------------------------------------------########## 32 | 33 | CC = avr-gcc 34 | OBJCOPY = avr-objcopy 35 | OBJDUMP = avr-objdump 36 | AVRSIZE = avr-size 37 | AVRDUDE = avrdude 38 | 39 | ##########------------------------------------------------------########## 40 | ########## Makefile Magic! ########## 41 | ########## Summary: ########## 42 | ########## We want a .hex file ########## 43 | ########## Compile source files into .elf ########## 44 | ########## Convert .elf file into .hex ########## 45 | ########## You shouldn't need to edit below. ########## 46 | ##########------------------------------------------------------########## 47 | 48 | ## The name of your project (without the .c) 49 | # TARGET = blinkLED 50 | ## Or name it automatically after the enclosing directory 51 | TARGET = $(lastword $(subst /, ,$(CURDIR))) 52 | 53 | 54 | # Object files: will find all .c/.h files in current directory 55 | # and in LIBDIR. If you have any other (sub-)directories with code, 56 | # you can add them in to SOURCES below in the wildcard statement. 57 | SOURCES=$(wildcard *.c $(LIBDIR)/*.c) 58 | OBJECTS=$(SOURCES:.c=.o) 59 | HEADERS=$(SOURCES:.c=.h) 60 | 61 | ## Compilation options, type man avr-gcc if you're curious. 62 | CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I. -I$(LIBDIR) 63 | CFLAGS = -Os -g -std=gnu99 -Wall 64 | ## Use short (8-bit) data types 65 | CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 66 | ## Splits up object files per function 67 | CFLAGS += -ffunction-sections -fdata-sections 68 | LDFLAGS = -Wl,-Map,$(TARGET).map 69 | ## Optional, but often ends up with smaller code 70 | LDFLAGS += -Wl,--gc-sections 71 | ## Relax shrinks code even more, but makes disassembly messy 72 | ## LDFLAGS += -Wl,--relax 73 | ## LDFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf 74 | ## LDFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf 75 | TARGET_ARCH = -mmcu=$(MCU) 76 | 77 | ## Default target: What to build 78 | ## all: $(TARGET).hex 79 | all: $(TARGET).hex disasm flash 80 | pyterm 81 | 82 | ## Explicit pattern rules: 83 | ## To make .o files from .c files 84 | %.o: %.c $(HEADERS) Makefile 85 | $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<; 86 | 87 | $(TARGET).elf: $(OBJECTS) 88 | $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ -o $@ 89 | 90 | %.hex: %.elf 91 | $(OBJCOPY) -j .text -j .data -O ihex $< $@ 92 | 93 | %.eeprom: %.elf 94 | $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ 95 | 96 | %.lst: %.elf 97 | $(OBJDUMP) -S $< > $@ 98 | 99 | 100 | debug: 101 | @echo 102 | @echo "Source files:" $(SOURCES) 103 | @echo "Target:" $(TARGET) 104 | @echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD) 105 | @echo 106 | 107 | # Optionally create listing file from .elf 108 | # This creates approximate assembly-language equivalent of your code. 109 | # Useful for debugging time-sensitive bits, 110 | # or making sure the compiler does what you want. 111 | disassemble: $(TARGET).lst 112 | 113 | disasm: disassemble 114 | 115 | # Optionally show how big the resulting program is 116 | size: $(TARGET).elf 117 | $(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf 118 | 119 | clean: 120 | rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \ 121 | $(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \ 122 | $(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \ 123 | $(TARGET).eeprom 124 | 125 | squeaky_clean: 126 | rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ *.eeprom 127 | 128 | ##########------------------------------------------------------########## 129 | ########## Programmer-specific details ########## 130 | ########## Flashing code to AVR using avrdude ########## 131 | ##########------------------------------------------------------########## 132 | 133 | flash: $(TARGET).hex 134 | $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$< 135 | 136 | ## An alias 137 | program: flash 138 | 139 | flash_eeprom: $(TARGET).eeprom 140 | $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$< 141 | 142 | avrdude_terminal: 143 | $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt 144 | 145 | ## If you've got multiple programmers that you use, 146 | ## you can define them here so that it's easy to switch. 147 | ## To invoke, use something like `make flash_arduinoISP` 148 | flash_usbtiny: PROGRAMMER_TYPE = usbtiny 149 | flash_usbtiny: PROGRAMMER_ARGS = # USBTiny works with no further arguments 150 | flash_usbtiny: flash 151 | 152 | flash_usbasp: PROGRAMMER_TYPE = usbasp 153 | flash_usbasp: PROGRAMMER_ARGS = # USBasp works with no further arguments 154 | flash_usbasp: flash 155 | 156 | flash_arduinoISP: PROGRAMMER_TYPE = avrisp 157 | flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0 158 | ## (for windows) flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P com5 159 | flash_arduinoISP: flash 160 | 161 | flash_109: PROGRAMMER_TYPE = avr109 162 | flash_109: PROGRAMMER_ARGS = -b 9600 -P /dev/ttyUSB0 163 | flash_109: flash 164 | 165 | ##########------------------------------------------------------########## 166 | ########## Fuse settings and suitable defaults ########## 167 | ##########------------------------------------------------------########## 168 | 169 | ## Mega 48, 88, 168, 328 default values 170 | LFUSE = 0x62 171 | HFUSE = 0xdf 172 | EFUSE = 0x00 173 | 174 | ## Generic 175 | FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m 176 | 177 | fuses: 178 | $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \ 179 | $(PROGRAMMER_ARGS) $(FUSE_STRING) 180 | show_fuses: 181 | $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv 182 | 183 | ## Called with no extra definitions, sets to defaults 184 | set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m 185 | set_default_fuses: fuses 186 | 187 | ## Set the fuse byte for full-speed mode 188 | ## Note: can also be set in firmware for modern chips 189 | set_fast_fuse: LFUSE = 0xE2 190 | set_fast_fuse: FUSE_STRING = -U lfuse:w:$(LFUSE):m 191 | set_fast_fuse: fuses 192 | 193 | ## Set the EESAVE fuse byte to preserve EEPROM across flashes 194 | set_eeprom_save_fuse: HFUSE = 0xD7 195 | set_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m 196 | set_eeprom_save_fuse: fuses 197 | 198 | ## Clear the EESAVE fuse byte 199 | clear_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m 200 | clear_eeprom_save_fuse: fuses 201 | 202 | 203 | 204 | 205 | 206 | ## These targets don't have files named after them 207 | .PHONY: all disassemble disasm eeprom size clean squeaky_clean flash fuses 208 | 209 | --------------------------------------------------------------------------------