├── .gitignore ├── .gitmodules ├── LICENSE.md ├── Makefile ├── PINS.md ├── PROTOCOL.md ├── README.md ├── bootloader ├── .gitignore ├── BootloaderAPI.c ├── BootloaderAPI.h ├── BootloaderAPITable.S ├── BootloaderDFU.c ├── BootloaderDFU.h ├── BootloaderDFU.txt ├── Config │ ├── AppConfig.h │ └── LUFAConfig.h ├── Descriptors.c ├── Descriptors.h ├── asf.xml ├── doxyfile └── makefile ├── buildchain.json ├── mk ├── atmega1280.mk ├── atmega2560.mk └── atmega32u4.mk ├── scripts ├── add_dfu_header.py └── lvfs.sh ├── src ├── battery.c ├── battery.h ├── cmd │ ├── battery.c │ ├── console.c │ ├── driver.c │ ├── echo.c │ ├── eeprom.c │ ├── exit.c │ ├── help.c │ ├── i2c.c │ ├── i2c_slave.c │ ├── keyboard.c │ ├── mod.c │ ├── mod.h │ ├── pin.c │ ├── pwm.c │ ├── sleep.c │ ├── thelio.c │ └── time.c ├── config.c ├── config.h ├── cpu.h ├── debounce.c ├── debounce.h ├── device.c ├── device.h ├── i2c │ ├── master.c │ ├── master.h │ ├── mod.c │ ├── mod.h │ ├── slave.c │ └── slave.h ├── keyboard │ ├── hid.h │ ├── map.c │ ├── mod.c │ ├── mod.h │ └── pins.c ├── macro.h ├── main.c ├── pin.c ├── pin.h ├── readline.c ├── readline.h ├── stdio.c ├── stdio.h ├── tach.c ├── tach.h ├── time.c ├── time.h ├── timer.c ├── timer.h ├── tokenize.c ├── tokenize.h ├── transition.c ├── transition.h ├── uart.c └── uart.h └── usb ├── .gitignore ├── Config └── LUFAConfig.h ├── Descriptors.c ├── Descriptors.h ├── LUFA USBtoSerial.inf ├── USBtoSerial.c ├── USBtoSerial.h ├── USBtoSerial.txt ├── asf.xml ├── doxyfile └── makefile /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lufa"] 2 | path = lufa 3 | url = https://github.com/system76/lufa.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEVICE?=atmega32u4 2 | #DEVICE?=atmega2560 3 | #DEVICE?=atmega1280 4 | REVISION=$(shell git describe --dirty --tags) 5 | 6 | include mk/$(DEVICE).mk 7 | 8 | BUILD=build/$(DEVICE) 9 | CFLAGS=\ 10 | -std=c11 \ 11 | -Os \ 12 | -fstack-usage \ 13 | -Wall \ 14 | -Wl,--gc-sections \ 15 | -Wl,-u,vfprintf \ 16 | -lprintf_flt \ 17 | -D __DEVICE__=$(DEVICE) \ 18 | -D __REVISION__=$(REVISION) \ 19 | -mmcu=$(DEVICE) 20 | 21 | .PHONY: all clean console dfu icsp stack 22 | 23 | all: $(BUILD)/boot.hex $(BUILD)/main.hex $(BUILD)/metadata.json 24 | 25 | # old CDC flashing method: 26 | #sudo avrdude -v -v -p $(DEVICE) -c $(PROGRAMMER) -P $(PORT) -b $(PROGRAMMER_BAUD) -D -U flash:w:$<:i 27 | 28 | clean: 29 | rm -rf $(BUILD) 30 | make -C bootloader clean 31 | make -C usb clean 32 | 33 | console: 34 | sudo tio $(PORT) -b $(CONSOLE_BAUD) 35 | 36 | dfu: $(BUILD)/main.hex 37 | sudo dfu-programmer $(DEVICE) flash $< 38 | sudo dfu-programmer $(DEVICE) reset 39 | 40 | icsp: $(BUILD)/boot.hex 41 | sudo avrdude -v -v \ 42 | -c usbasp \ 43 | -p $(DEVICE) \ 44 | -U flash:w:$<:i\ 45 | -U lfuse:w:0xFF:m \ 46 | -U hfuse:w:0xD9:m \ 47 | -U efuse:w:0xCB:m 48 | 49 | stack: $(BUILD)/main.elf 50 | sort -k2 -n $$(find $(BUILD) -name *.su) 51 | 52 | $(BUILD)/%.o: src/%.c src/*.h src/*/*.h 53 | mkdir -p $(@D) 54 | avr-gcc $(CFLAGS) $< -c -o $@ 55 | 56 | $(BUILD)/libUSBtoSerial.a: usb/makefile usb/*.c usb/*.h usb/Config/*.h 57 | mkdir -p $(@D) 58 | make -C usb lib 59 | cp usb/libUSBtoSerial.a $@ 60 | 61 | OBJS=\ 62 | $(BUILD)/cmd/thelio.o \ 63 | $(BUILD)/config.o \ 64 | $(BUILD)/debounce.o \ 65 | $(BUILD)/device.o \ 66 | $(BUILD)/pin.o \ 67 | $(BUILD)/stdio.o \ 68 | $(BUILD)/tach.o \ 69 | $(BUILD)/time.o \ 70 | $(BUILD)/timer.o \ 71 | $(BUILD)/transition.o \ 72 | $(BUILD)/uart.o \ 73 | $(BUILD)/main.o \ 74 | $(BUILD)/libUSBtoSerial.a 75 | 76 | #\ 77 | $(BUILD)/cmd/mod.o \ 78 | $(BUILD)/cmd/console.o \ 79 | $(BUILD)/cmd/battery.o \ 80 | $(BUILD)/cmd/driver.o \ 81 | $(BUILD)/cmd/echo.o \ 82 | $(BUILD)/cmd/eeprom.o \ 83 | $(BUILD)/cmd/exit.o \ 84 | $(BUILD)/cmd/help.o \ 85 | $(BUILD)/cmd/i2c.o \ 86 | $(BUILD)/cmd/i2c_slave.o \ 87 | $(BUILD)/cmd/keyboard.o \ 88 | $(BUILD)/cmd/pin.o \ 89 | $(BUILD)/cmd/pwm.o \ 90 | $(BUILD)/cmd/sleep.o \ 91 | $(BUILD)/cmd/time.o \ 92 | $(BUILD)/i2c/mod.o \ 93 | $(BUILD)/i2c/master.o \ 94 | $(BUILD)/i2c/slave.o \ 95 | $(BUILD)/keyboard/mod.o \ 96 | $(BUILD)/keyboard/map.o \ 97 | $(BUILD)/keyboard/pins.o \ 98 | $(BUILD)/battery.o \ 99 | $(BUILD)/readline.o \ 100 | $(BUILD)/tokenize.o \ 101 | 102 | $(BUILD)/main.elf: $(OBJS) 103 | mkdir -p $(@D) 104 | avr-gcc $(CFLAGS) $^ -o $@ 105 | 106 | $(BUILD)/boot.hex: bootloader/makefile bootloader/*.c bootloader/*.h bootloader/Config/*.h 107 | mkdir -p $(@D) 108 | make -C bootloader hex 109 | cp bootloader/BootloaderDFU.hex $@ 110 | 111 | $(BUILD)/main.hex: $(BUILD)/main.elf 112 | mkdir -p $(@D) 113 | avr-objcopy -j .text -j .data -O ihex $< $@ 114 | 115 | $(BUILD)/main.bin: $(BUILD)/main.hex 116 | mkdir -p $(@D) 117 | avr-objcopy -I ihex -O binary $< $@ 118 | 119 | $(BUILD)/metadata.json: 120 | mkdir -p $(@D) 121 | echo "{ \"device\": \"$(DEVICE)\", \"revision\": \"$(REVISION)\" }" > $@ 122 | -------------------------------------------------------------------------------- /PINS.md: -------------------------------------------------------------------------------- 1 | ## Arduino Micro 2 | 3 | ``` 4 | 1. D7 -> PE6 -> CPUMUX0 !A/B 5 | 28. D8 -> PB4 -> PMB0 SW+ 6 | 29. D9 -> PB5 -> OC1A -> CPUMUX0 B : CPUMUX Y -> CPUOUT0 PWM 7 | 30. D10 -> PB6 -> OC1B -> INTAKE0 PWM 8 | 31. D5 -> PC6 -> OC3A -> EXHAUST0 PWM 9 | 32. D13 -> PC7 -> OC4A -> R2 2 : R2 1 -> PFP0 LED+ 10 | 37. A1 -> PF6 -> ADC6 -> EXHAUST0 TACH 11 | 38. A2 -> PF5-> ADC5 -> INTAKE0 TACH 12 | 39. A3 -> PF4 -> ADC4 -> CPUOUT0 TACH 13 | 40. A4 -> PF1 -> ADC1 -> PFP0 SW+ 14 | 41. A5 -> PF0 -> ADC0 -> PMB0 LED+ 15 | ``` 16 | 17 | ## AVR0 18 | 19 | ``` 20 | 1. PE6 -> PE6 21 | 28. PB4 -> PB4 22 | 29. PB5 -> OC1A 23 | 30. PB6 -> OC1B 24 | 31. PC6 -> OC3A 25 | 32. PC7 -> OC4A 26 | 37. PF6 -> ADC6 27 | 38. PF5 -> ADC5 28 | 39. PF4 -> ADC4 29 | 40. PF1 -> ADC1 30 | 41. PF0 -> ADC0 31 | ``` 32 | 33 | ## CPUMUX0 34 | 35 | ``` 36 | 1. A -> CPUIN0 PWM 37 | 2. B -> OC1A 38 | 3. !Y 39 | 4. VCC -> VCC 40 | 5. Y -> CPUOUT0 PWM 41 | 6. !A/B -> PE6 42 | 7. GND -> GND 43 | ``` 44 | 45 | ## CPUIN0 46 | 47 | ``` 48 | 1. GND -> GND 49 | 2. 12V 50 | 3. TACH -> ADC4 51 | 4. PWM -> CPUMUX0 A 52 | ``` 53 | 54 | ## CPUOUT0 55 | 56 | ``` 57 | 1. GND -> GND 58 | 2. 12V -> 12V 59 | 3. TACH -> ADC4 60 | 4. PWM -> CPUMUX0 Y 61 | ``` 62 | 63 | ## INTAKE0 64 | 65 | ``` 66 | 1. GND -> GND 67 | 2. 12V -> 12V 68 | 3. TACH -> ADC5 69 | 4. PWM -> OC1B 70 | ``` 71 | 72 | ## EXHAUST0 73 | 74 | ``` 75 | 1. GND -> GND 76 | 2. 12V -> 12V 77 | 3. TACH -> ADC6 78 | 4. PWM -> OC3A 79 | ``` 80 | 81 | ## PFP0 82 | 83 | ``` 84 | 1. SW- -> GND 85 | 2. SW+ -> ADC1 86 | 3. LED- -> GND 87 | 4. SW+ -> R2 1 88 | ``` 89 | 90 | ## PMB0 91 | 92 | ``` 93 | 1. SW- -> GND 94 | 2. SW+ -> PB4 95 | 3. LED- -> GND 96 | 4. LED+ -> ADC0 97 | ``` 98 | 99 | ## R2 100 | 101 | ``` 102 | 1. 1 -> PFP0 LED+ 103 | 2. 2 -> OC4A 104 | ``` 105 | 106 | ## B0 107 | -------------------------------------------------------------------------------- /PROTOCOL.md: -------------------------------------------------------------------------------- 1 | # USB Protocol 2 | 3 | The Thelio Io module uses USB CDC ACM in order to communicate with the host. 4 | This is an easy protocol to handle on both the host and device side, and is 5 | used for devices such as GPS and OBDII. Normal AT commands are ignored, ERROR 6 | is returned. Instead, a custom set of commands is used tailored to the 7 | application. 8 | 9 | The Thelio Io device can be found with an ID of 7676:7676, to be allocated 10 | later. Opening this device and setting it up with a frequency of 1 MHz will 11 | allow you to start sending commands. Other frequencies may be possible, but 12 | the design should utilize 1 MHz if possible. 13 | 14 | Commands are 15 bytes in length, and have the format: `IoCCCCDDDDDDDD\r`. Here, 15 | the `Io` indicates a literal two-byte ASCII `Io` marker, similar to `AT` in AT 16 | commands. `CCCC` indicates a four-byte ASCII command, which may be a hex string, 17 | and `DDDDDDDD` indicates an eight-byte ASCII data value, which may also be a 18 | hex string. The data value is interpreted based on the command, and may contain 19 | subcommands or multiple values. It may also be truncated. 20 | 21 | Upon receiving a command, the embedded controller will instantly execute the 22 | command and will print output, followed by either `\r\nOK\r\n` or 23 | `\r\nERROR\r\n`, similar to `AT` command output. The host should wait for one of 24 | these return codes before issuing another command. 25 | 26 | Device identifiers must be known by the enumerator, using the USB ID. Attempting 27 | to read the tachometer setting is an easy way to check for a device being 28 | present. In the default device, the following device identifiers are available: 29 | 30 | - `CPUF` - CPU fan 31 | - `INTF` - intake fan 32 | - `EXHF` - exhaust fan 33 | - `POWB` - power button 34 | 35 | # Examples 36 | 37 | ## Tachometer 38 | 39 | `IoTACHCPUF\r` 40 | 41 | Command: `TACH` 42 | Data: `CPUF` 43 | Return: `\r\n07D0\r\n\r\nOK\r\n` 44 | 45 | Get the tachometer of device `CPUF`. This will return the tachometer reading 46 | in hex, in this case, it is `2000`, followed by `OK`. 47 | 48 | If the device is not found, `ERROR` will be returned. 49 | 50 | ## Duty cycle (Get) 51 | 52 | `IoDUTYCPUF\r` 53 | 54 | Command: `DUTY` 55 | Data: `CPUF` 56 | Return: `\r\n1388\r\n\r\nOK\r\n` 57 | 58 | Get the duty of device `CPUF`. This will return the current duty cycle, in this 59 | case, `5000`, which is 50%, followed by `OK`. This value may still be 60 | transitioning due to a previous duty cycle set command. 61 | 62 | If the device is not found, `ERROR` will be returned. 63 | 64 | ## Duty cycle (Set) 65 | 66 | `IoDUTYCPUF2710\r` 67 | 68 | Command: `DUTY` 69 | Data: `CPUF2710` 70 | Return: `\r\nOK\r\n` 71 | 72 | Set the duty of device `CPUF` to `10000`, which is 100%. The data must be a hex 73 | encoded value from `0000` to `2710`. 74 | 75 | If the device is not found, or the value is above `2710` in hex, `ERROR` will be 76 | returned. 77 | 78 | 79 | ## Suspend State (Get) 80 | 81 | `IoSUSP\r` 82 | 83 | Command: `SUSP` 84 | Data: `` 85 | Return: `\r\n0001\r\n\r\nOK\r\n` 86 | 87 | Get the suspend state of the device. This will return a hex encoded suspend 88 | state. Currently the only values supported are `0000` and `0001`. 89 | 90 | Errors should not occur, but must be handled in case of a firmware modification. 91 | 92 | ## Power State (Set) 93 | 94 | `IoSUSP0001\r` 95 | 96 | Command: `SUSP` 97 | Data: `0001` 98 | Return: `\r\nOK\r\n` 99 | 100 | Set the suspend state of the device. The data must be a hex encoded value. 101 | Currently the only values supported are `0000` and `0001`. 102 | 103 | If the value is above `0001` in hex, `ERROR` will be returned. 104 | 105 | 106 | ## Revision (Get) 107 | 108 | `IoREVISION\r` 109 | 110 | Command: `REVISION` 111 | Data: `` 112 | Return: `\r\n1.0.0\r\n\r\nOK\r\n` 113 | 114 | Get the revision of the device. This will return a string, created by the command 115 | `git describe --tags --dirty`. This command is 8 characters long to prevent the 116 | incorrect use of a set command. 117 | 118 | Errors should not occur, but must be handled in case of a firmware modification. 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Thelio Io Firmware 2 | 3 | This repository contains firmware, in C, for the System76 Thelio Io device. It is possible to prototype the device using an Arduino Micro. 4 | -------------------------------------------------------------------------------- /bootloader/.gitignore: -------------------------------------------------------------------------------- 1 | /obj/ 2 | *.a 3 | *.bin 4 | *.eep 5 | *.elf 6 | *.hex 7 | *.lss 8 | *.map 9 | *.sym 10 | -------------------------------------------------------------------------------- /bootloader/BootloaderAPI.c: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Bootloader user application API functions. 34 | */ 35 | 36 | #include "BootloaderAPI.h" 37 | 38 | static bool IsPageAddressValid(const uint32_t Address) 39 | { 40 | /* Determine if the given page address is correctly aligned to the 41 | start of a flash page. */ 42 | bool PageAddressIsAligned = !(Address & (SPM_PAGESIZE - 1)); 43 | 44 | return (Address < BOOT_START_ADDR) && PageAddressIsAligned; 45 | } 46 | 47 | void BootloaderAPI_ErasePage(const uint32_t Address) 48 | { 49 | if (! IsPageAddressValid(Address)) 50 | return; 51 | 52 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 53 | { 54 | boot_page_erase_safe(Address); 55 | boot_spm_busy_wait(); 56 | boot_rww_enable(); 57 | } 58 | } 59 | 60 | void BootloaderAPI_WritePage(const uint32_t Address) 61 | { 62 | if (! IsPageAddressValid(Address)) 63 | return; 64 | 65 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 66 | { 67 | boot_page_write_safe(Address); 68 | boot_spm_busy_wait(); 69 | boot_rww_enable(); 70 | } 71 | } 72 | 73 | void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word) 74 | { 75 | boot_page_fill_safe(Address, Word); 76 | } 77 | 78 | uint8_t BootloaderAPI_ReadSignature(const uint16_t Address) 79 | { 80 | return boot_signature_byte_get(Address); 81 | } 82 | 83 | uint8_t BootloaderAPI_ReadFuse(const uint16_t Address) 84 | { 85 | return boot_lock_fuse_bits_get(Address); 86 | } 87 | 88 | uint8_t BootloaderAPI_ReadLock(void) 89 | { 90 | return boot_lock_fuse_bits_get(GET_LOCK_BITS); 91 | } 92 | 93 | void BootloaderAPI_WriteLock(const uint8_t LockBits) 94 | { 95 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 96 | { 97 | boot_lock_bits_set_safe(LockBits); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /bootloader/BootloaderAPI.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for BootloaderAPI.c. 34 | */ 35 | 36 | #ifndef _BOOTLOADER_API_H_ 37 | #define _BOOTLOADER_API_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | 47 | #include "Config/AppConfig.h" 48 | 49 | /* Function Prototypes: */ 50 | void BootloaderAPI_ErasePage(const uint32_t Address); 51 | void BootloaderAPI_WritePage(const uint32_t Address); 52 | void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word); 53 | uint8_t BootloaderAPI_ReadSignature(const uint16_t Address); 54 | uint8_t BootloaderAPI_ReadFuse(const uint16_t Address); 55 | uint8_t BootloaderAPI_ReadLock(void); 56 | void BootloaderAPI_WriteLock(const uint8_t LockBits); 57 | 58 | #endif 59 | 60 | -------------------------------------------------------------------------------- /bootloader/BootloaderAPITable.S: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | ; Trampolines to actual API implementations if the target address is outside the 32 | ; range of a rjmp instruction (can happen with large bootloader sections) 33 | .section .apitable_trampolines, "ax" 34 | .global BootloaderAPI_Trampolines 35 | BootloaderAPI_Trampolines: 36 | 37 | BootloaderAPI_ErasePage_Trampoline: 38 | jmp BootloaderAPI_ErasePage 39 | BootloaderAPI_WritePage_Trampoline: 40 | jmp BootloaderAPI_WritePage 41 | BootloaderAPI_FillWord_Trampoline: 42 | jmp BootloaderAPI_FillWord 43 | BootloaderAPI_ReadSignature_Trampoline: 44 | jmp BootloaderAPI_ReadSignature 45 | BootloaderAPI_ReadFuse_Trampoline: 46 | jmp BootloaderAPI_ReadFuse 47 | BootloaderAPI_ReadLock_Trampoline: 48 | jmp BootloaderAPI_ReadLock 49 | BootloaderAPI_WriteLock_Trampoline: 50 | jmp BootloaderAPI_WriteLock 51 | BootloaderAPI_UNUSED1: 52 | ret 53 | BootloaderAPI_UNUSED2: 54 | ret 55 | BootloaderAPI_UNUSED3: 56 | ret 57 | BootloaderAPI_UNUSED4: 58 | ret 59 | BootloaderAPI_UNUSED5: 60 | ret 61 | 62 | 63 | 64 | ; API function jump table 65 | .section .apitable_jumptable, "ax" 66 | .global BootloaderAPI_JumpTable 67 | BootloaderAPI_JumpTable: 68 | 69 | rjmp BootloaderAPI_ErasePage_Trampoline 70 | rjmp BootloaderAPI_WritePage_Trampoline 71 | rjmp BootloaderAPI_FillWord_Trampoline 72 | rjmp BootloaderAPI_ReadSignature_Trampoline 73 | rjmp BootloaderAPI_ReadFuse_Trampoline 74 | rjmp BootloaderAPI_ReadLock_Trampoline 75 | rjmp BootloaderAPI_WriteLock_Trampoline 76 | rjmp BootloaderAPI_UNUSED1 ; UNUSED ENTRY 1 77 | rjmp BootloaderAPI_UNUSED2 ; UNUSED ENTRY 2 78 | rjmp BootloaderAPI_UNUSED3 ; UNUSED ENTRY 3 79 | rjmp BootloaderAPI_UNUSED4 ; UNUSED ENTRY 4 80 | rjmp BootloaderAPI_UNUSED5 ; UNUSED ENTRY 5 81 | 82 | 83 | 84 | ; Bootloader table signatures and information 85 | .section .apitable_signatures, "ax" 86 | .global BootloaderAPI_Signatures 87 | BootloaderAPI_Signatures: 88 | 89 | .long BOOT_START_ADDR ; Start address of the bootloader 90 | .word 0xDF10 ; Signature for the DFU class bootloader, V1 91 | .word 0xDCFB ; Signature for a LUFA class bootloader 92 | -------------------------------------------------------------------------------- /bootloader/BootloaderDFU.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for BootloaderDFU.c. 34 | */ 35 | 36 | #ifndef _BOOTLOADER_H_ 37 | #define _BOOTLOADER_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include "Descriptors.h" 51 | #include "BootloaderAPI.h" 52 | #include "Config/AppConfig.h" 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | /* Preprocessor Checks: */ 59 | #if !defined(__OPTIMIZE_SIZE__) 60 | #error This bootloader requires that it be optimized for size, not speed, to fit into the target device. Change optimization settings and try again. 61 | #endif 62 | 63 | /* Macros: */ 64 | /** Major bootloader version number. */ 65 | #define BOOTLOADER_VERSION_MINOR 2 66 | 67 | /** Minor bootloader version number. */ 68 | #define BOOTLOADER_VERSION_REV 0 69 | 70 | /** Magic bootloader key to unlock forced application start mode. */ 71 | #define MAGIC_BOOT_KEY 0xDC42 72 | 73 | /** Complete bootloader version number expressed as a packed byte, constructed from the 74 | * two individual bootloader version macros. 75 | */ 76 | #define BOOTLOADER_VERSION ((BOOTLOADER_VERSION_MINOR << 4) | BOOTLOADER_VERSION_REV) 77 | 78 | /** First byte of the bootloader identification bytes, used to identify a device's bootloader. */ 79 | #define BOOTLOADER_ID_BYTE1 0xDC 80 | 81 | /** Second byte of the bootloader identification bytes, used to identify a device's bootloader. */ 82 | #define BOOTLOADER_ID_BYTE2 0xFB 83 | 84 | /** Convenience macro, used to determine if the issued command is the given one-byte long command. 85 | * 86 | * \param[in] dataarr Command byte array to check against 87 | * \param[in] cb1 First command byte to check 88 | */ 89 | #define IS_ONEBYTE_COMMAND(dataarr, cb1) (dataarr[0] == (cb1)) 90 | 91 | /** Convenience macro, used to determine if the issued command is the given two-byte long command. 92 | * 93 | * \param[in] dataarr Command byte array to check against 94 | * \param[in] cb1 First command byte to check 95 | * \param[in] cb2 Second command byte to check 96 | */ 97 | #define IS_TWOBYTE_COMMAND(dataarr, cb1, cb2) ((dataarr[0] == (cb1)) && (dataarr[1] == (cb2))) 98 | 99 | /** Length of the DFU file suffix block, appended to the end of each complete memory write command. 100 | * The DFU file suffix is currently unused (but is designed to give extra file information, such as 101 | * a CRC of the complete firmware for error checking) and so is discarded. 102 | */ 103 | #define DFU_FILE_SUFFIX_SIZE 16 104 | 105 | /** Length of the DFU file filler block, appended to the start of each complete memory write command. 106 | * Filler bytes are added to the start of each complete memory write command, and must be discarded. 107 | */ 108 | #define DFU_FILLER_BYTES_SIZE 26 109 | 110 | /** DFU class command request to detach from the host. */ 111 | #define DFU_REQ_DETATCH 0x00 112 | 113 | /** DFU class command request to send data from the host to the bootloader. */ 114 | #define DFU_REQ_DNLOAD 0x01 115 | 116 | /** DFU class command request to send data from the bootloader to the host. */ 117 | #define DFU_REQ_UPLOAD 0x02 118 | 119 | /** DFU class command request to get the current DFU status and state from the bootloader. */ 120 | #define DFU_REQ_GETSTATUS 0x03 121 | 122 | /** DFU class command request to reset the current DFU status and state variables to their defaults. */ 123 | #define DFU_REQ_CLRSTATUS 0x04 124 | 125 | /** DFU class command request to get the current DFU state of the bootloader. */ 126 | #define DFU_REQ_GETSTATE 0x05 127 | 128 | /** DFU class command request to abort the current multi-request transfer and return to the dfuIDLE state. */ 129 | #define DFU_REQ_ABORT 0x06 130 | 131 | /** DFU command to begin programming the device's memory. */ 132 | #define COMMAND_PROG_START 0x01 133 | 134 | /** DFU command to begin reading the device's memory. */ 135 | #define COMMAND_DISP_DATA 0x03 136 | 137 | /** DFU command to issue a write command. */ 138 | #define COMMAND_WRITE 0x04 139 | 140 | /** DFU command to issue a read command. */ 141 | #define COMMAND_READ 0x05 142 | 143 | /** DFU command to issue a memory base address change command, to set the current 64KB flash page 144 | * that subsequent flash operations should use. */ 145 | #define COMMAND_CHANGE_BASE_ADDR 0x06 146 | 147 | /* Type Defines: */ 148 | /** Type define for a non-returning function pointer to the loaded application. */ 149 | typedef void (*AppPtr_t)(void) ATTR_NO_RETURN; 150 | 151 | /** Type define for a structure containing a complete DFU command issued by the host. */ 152 | typedef struct 153 | { 154 | uint8_t Command; /**< Single byte command to perform, one of the \c COMMAND_* macro values */ 155 | uint8_t Data[5]; /**< Command parameters */ 156 | uint16_t DataSize; /**< Size of the command parameters */ 157 | } DFU_Command_t; 158 | 159 | /* Enums: */ 160 | /** DFU bootloader states. Refer to the DFU class specification for information on each state. */ 161 | enum DFU_State_t 162 | { 163 | appIDLE = 0, 164 | appDETACH = 1, 165 | dfuIDLE = 2, 166 | dfuDNLOAD_SYNC = 3, 167 | dfuDNBUSY = 4, 168 | dfuDNLOAD_IDLE = 5, 169 | dfuMANIFEST_SYNC = 6, 170 | dfuMANIFEST = 7, 171 | dfuMANIFEST_WAIT_RESET = 8, 172 | dfuUPLOAD_IDLE = 9, 173 | dfuERROR = 10 174 | }; 175 | 176 | /** DFU command status error codes. Refer to the DFU class specification for information on each error code. */ 177 | enum DFU_Status_t 178 | { 179 | OK = 0, 180 | errTARGET = 1, 181 | errFILE = 2, 182 | errWRITE = 3, 183 | errERASE = 4, 184 | errCHECK_ERASED = 5, 185 | errPROG = 6, 186 | errVERIFY = 7, 187 | errADDRESS = 8, 188 | errNOTDONE = 9, 189 | errFIRMWARE = 10, 190 | errVENDOR = 11, 191 | errUSBR = 12, 192 | errPOR = 13, 193 | errUNKNOWN = 14, 194 | errSTALLEDPKT = 15 195 | }; 196 | 197 | /* Function Prototypes: */ 198 | static void SetupHardware(void); 199 | static void ResetHardware(void); 200 | 201 | void EVENT_USB_Device_ControlRequest(void); 202 | 203 | #if defined(INCLUDE_FROM_BOOTLOADER_C) 204 | static void DiscardFillerBytes(uint8_t NumberOfBytes); 205 | static void ProcessBootloaderCommand(void); 206 | static void LoadStartEndAddresses(void); 207 | static void ProcessMemProgCommand(void); 208 | static void ProcessMemReadCommand(void); 209 | static void ProcessWriteCommand(void); 210 | static void ProcessReadCommand(void); 211 | #endif 212 | 213 | void Application_Jump_Check(void) ATTR_INIT_SECTION(3); 214 | 215 | #endif 216 | 217 | -------------------------------------------------------------------------------- /bootloader/BootloaderDFU.txt: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3 | * This file contains special DoxyGen information for the generation of the main page and other special 4 | * documentation pages. It is not a project source file. 5 | */ 6 | 7 | /** \mainpage DFU Class USB AVR Bootloader 8 | * 9 | * \section Sec_Compat Demo Compatibility: 10 | * 11 | * The following list indicates what microcontrollers are compatible with this demo. 12 | * 13 | * \li Series 7 USB AVRs (AT90USBxxx7) 14 | * \li Series 6 USB AVRs (AT90USBxxx6) 15 | * \li Series 4 USB AVRs (ATMEGAxxU4) - See \ref SSec_Aux_Space 16 | * \li Series 2 USB AVRs (AT90USBxx2, ATMEGAxxU2) - See \ref SSec_Aux_Space 17 | * 18 | * \section Sec_Info USB Information: 19 | * 20 | * The following table gives a rundown of the USB utilization of this demo. 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 43 | * 44 | *
USB Mode:Device
USB Class:Device Firmware Update Class (DFU)
USB Subclass:None
Relevant Standards:USBIF DFU Class Standard, Atmel USB Bootloader Datasheet
Supported USB Speeds:Low Speed Mode \n 42 | * Full Speed Mode
45 | * 46 | * \section Sec_Description Project Description: 47 | * 48 | * This bootloader enumerates to the host as a DFU Class device, allowing for DFU-compatible programming 49 | * software to load firmware onto the AVR. 50 | * 51 | * Out of the box this bootloader builds for the AT90USB1287 with an 8KB bootloader section size, and will fit 52 | * into 4KB of bootloader space. If you wish to alter this size and/or change the AVR model, you will need to 53 | * edit the MCU, FLASH_SIZE_KB and BOOT_SECTION_SIZE_KB values in the accompanying makefile. 54 | * 55 | * When the bootloader is running, the board's LED(s) will flash at regular intervals to distinguish the 56 | * bootloader from the normal user application. 57 | * 58 | * \section Sec_Running Running the Bootloader 59 | * 60 | * On the USB AVR8 devices, setting the \c HWBE device fuse will cause the bootloader to run if the \c HWB pin of 61 | * the AVR is grounded when the device is reset. 62 | * 63 | * The are two behaviours of this bootloader, depending on the device's fuses: 64 | * 65 | * If the device's BOOTRST fuse is set, the bootloader will run any time the system is reset from 66 | * the external reset pin, unless no valid user application has been loaded. To initiate the bootloader, the 67 | * device's external reset pin should be grounded momentarily. 68 | * 69 | * If the device's BOOTRST fuse is not set, the bootloader will run only if initiated via a software 70 | * jump, or if the \c HWB pin was low during the last device reset (if the \c HWBE fuse is set). 71 | * 72 | * For board specific exceptions to the above, see below. 73 | * 74 | * \subsection SSec_XPLAIN Atmel Xplain Board 75 | * Ground the USB AVR JTAG's \c TCK pin to ground when powering on the board to start the bootloader. This assumes the 76 | * \c HWBE fuse is cleared and the \c BOOTRST fuse is set as the HWBE pin is not user accessible on this board. 77 | * 78 | * \subsection SSec_Leonardo Arduino Leonardo Board 79 | * Ground \c IO13 when powering the board to start the bootloader. This assumes the \c HWBE fuse is cleared and the 80 | * \c BOOTRST fuse is set as the HWBE pin is not user accessible on this board. 81 | * 82 | * \section Sec_Installation Driver Installation 83 | * 84 | * This bootloader is designed to be compatible with Atmel's provided Windows DFU class drivers. You will need to 85 | * install Atmel's DFU drivers prior to using this bootloader on Windows platforms. If you are using a 64 bit Windows 86 | * OS, you will need to either disable the driver signing requirement (see online tutorials for details) or use a 87 | * digitally signed version of the official Atmel driver provided by a third party AVR user at 88 | * http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_id=2196&item_type=project. 89 | * 90 | * \note This device spoofs Atmel's DFU Bootloader USB VID and PID so that the Atmel DFU bootloader 91 | * drivers included with FLIP will work. If you do not wish to use Atmel's ID codes, please 92 | * manually change them in Descriptors.c and alter your driver's INF file accordingly. 93 | * 94 | * \section Sec_HostApp Host Controller Application 95 | * 96 | * This bootloader is compatible with Atmel's FLIP utility on Windows machines, and dfu-programmer on Linux machines. 97 | * 98 | * \subsection SSec_FLIP FLIP (Windows) 99 | * 100 | * FLIP (Flexible In-System Programmer) is a utility written by Atmel, and distributed for free on the Atmel website. 101 | * The FLIP utility is designed to assist in the bootloader programming of a range of Atmel devices, through several 102 | * popular physical interfaces including USB. It is written in Java, however makes use of native extensions for USB 103 | * support and thus is only offered on Windows. 104 | * 105 | * To program a device using FLIP, refer to the Atmel FLIP documentation. 106 | * 107 | * \subsection SSec_DFUProgrammer dfu-programmer (Linux) 108 | * 109 | * dfu-programmer is an open-source command line solution for the bootloader programming of Atmel devices through a 110 | * USB connection, using the DFU protocol, available for download at http://sourceforge.net/projects/dfu-programmer/. 111 | * 112 | * The following example loads a HEX file into the AVR's FLASH memory using dfu-programmer: 113 | * \code 114 | * dfu-programmer at90usb1287 erase flash Mouse.hex 115 | * \endcode 116 | * 117 | * \section Sec_API User Application API 118 | * 119 | * Several user application functions for FLASH and other special memory area manipulations are exposed by the bootloader, 120 | * allowing the user application to call into the bootloader at runtime to read and write FLASH data. 121 | * 122 | * \warning The APIs exposed by the DFU class bootloader are \b NOT compatible with the API exposed by the official Atmel DFU bootloader. 123 | * 124 | * By default, the bootloader API jump table is located 32 bytes from the end of the device's FLASH memory, and follows the 125 | * following layout: 126 | * 127 | * \code 128 | * #define BOOTLOADER_API_TABLE_SIZE 32 129 | * #define BOOTLOADER_API_TABLE_START ((FLASHEND + 1UL) - BOOTLOADER_API_TABLE_SIZE) 130 | * #define BOOTLOADER_API_CALL(Index) (void*)((BOOTLOADER_API_TABLE_START + (Index * 2)) / 2) 131 | * 132 | * void (*BootloaderAPI_ErasePage)(uint32_t Address) = BOOTLOADER_API_CALL(0); 133 | * void (*BootloaderAPI_WritePage)(uint32_t Address) = BOOTLOADER_API_CALL(1); 134 | * void (*BootloaderAPI_FillWord)(uint32_t Address, uint16_t Word) = BOOTLOADER_API_CALL(2); 135 | * uint8_t (*BootloaderAPI_ReadSignature)(uint16_t Address) = BOOTLOADER_API_CALL(3); 136 | * uint8_t (*BootloaderAPI_ReadFuse)(uint16_t Address) = BOOTLOADER_API_CALL(4); 137 | * uint8_t (*BootloaderAPI_ReadLock)(void) = BOOTLOADER_API_CALL(5); 138 | * void (*BootloaderAPI_WriteLock)(uint8_t LockBits) = BOOTLOADER_API_CALL(6); 139 | * 140 | * #define BOOTLOADER_MAGIC_SIGNATURE_START (BOOTLOADER_API_TABLE_START + (BOOTLOADER_API_TABLE_SIZE - 2)) 141 | * #define BOOTLOADER_MAGIC_SIGNATURE 0xDCFB 142 | * 143 | * #define BOOTLOADER_CLASS_SIGNATURE_START (BOOTLOADER_API_TABLE_START + (BOOTLOADER_API_TABLE_SIZE - 4)) 144 | * #define BOOTLOADER_DFU_SIGNATURE 0xDF10 145 | * 146 | * #define BOOTLOADER_ADDRESS_START (BOOTLOADER_API_TABLE_START + (BOOTLOADER_API_TABLE_SIZE - 8)) 147 | * #define BOOTLOADER_ADDRESS_LENGTH 4 148 | * \endcode 149 | * 150 | * From the application the API support of the bootloader can be detected by reading the FLASH memory bytes located at address 151 | * \c BOOTLOADER_MAGIC_SIGNATURE_START and comparing them to the value \c BOOTLOADER_MAGIC_SIGNATURE. The class of bootloader 152 | * can be determined by reading the FLASH memory bytes located at address \c BOOTLOADER_CLASS_SIGNATURE_START and comparing them 153 | * to the value \c BOOTLOADER_DFU_SIGNATURE. The start address of the bootloader can be retrieved by reading the bytes of FLASH 154 | * memory starting from address \c BOOTLOADER_ADDRESS_START. 155 | * 156 | * \subsection SSec_API_MemLayout Device Memory Map 157 | * The following illustration indicates the final memory map of the device when loaded with the bootloader. 158 | * 159 | * \verbatim 160 | * +----------------------------+ 0x0000 161 | * | | 162 | * | | 163 | * | | 164 | * | | 165 | * | | 166 | * | | 167 | * | | 168 | * | | 169 | * | User Application | 170 | * | | 171 | * | | 172 | * | | 173 | * | | 174 | * | | 175 | * | | 176 | * | | 177 | * | | 178 | * +----------------------------+ FLASHEND - BOOT_AUX_SECTION_SIZE 179 | * | Booloader Start Trampoline | 180 | * | (Not User App. Accessible) | 181 | * +----------------------------+ FLASHEND - (BOOT_AUX_SECTION_SIZE - 4) 182 | * | | 183 | * | Auxillery Bootloader | 184 | * | Space for Smaller Devices | 185 | * | (Not User App. Accessible) | 186 | * | | 187 | * +----------------------------+ FLASHEND - BOOT_SECTION_SIZE 188 | * | | 189 | * | Bootloader Application | 190 | * | (Not User App. Accessible) | 191 | * | | 192 | * +----------------------------+ FLASHEND - 96 193 | * | API Table Trampolines | 194 | * | (Not User App. Accessible) | 195 | * +----------------------------+ FLASHEND - 32 196 | * | Bootloader API Table | 197 | * | (User App. Accessible) | 198 | * +----------------------------+ FLASHEND - 8 199 | * | Bootloader ID Constants | 200 | * | (User App. Accessible) | 201 | * +----------------------------+ FLASHEND 202 | * \endverbatim 203 | * 204 | * \subsection SSec_Aux_Space Auxiliary Bootloader Section 205 | * To make the bootloader function on smaller devices (those with a physical 206 | * bootloader section of smaller than 6KB) 207 | * 208 | * \section Sec_KnownIssues Known Issues: 209 | * 210 | * \par On Linux machines, the DFU bootloader is inaccessible. 211 | * On many Linux systems, non-root users do not have automatic access to newly 212 | * inserted DFU devices. Root privileges or a UDEV rule is required to gain 213 | * access. 214 | * See here for resolution steps. 215 | * 216 | * \section Sec_Options Project Options 217 | * 218 | * The following defines can be found in this demo, which can control the demo behaviour when defined, or changed in value. 219 | * 220 | * 221 | * 222 | * 223 | * 224 | * 225 | * 226 | * 227 | * 228 | * 229 | * 232 | * 233 | *
Define Name:Location:Description:
SECURE_MODEAppConfig.hIf defined to \c true, the bootloader will not accept any memory commands other than a chip erase on start-up, until an 230 | * erase has been performed. This can be used in conjunction with the AVR's lockbits to prevent the AVRs firmware from 231 | * being dumped by unauthorized persons. When false, all memory operations are allowed at any time.
234 | */ 235 | 236 | -------------------------------------------------------------------------------- /bootloader/Config/AppConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * \brief Application Configuration Header File 33 | * 34 | * This is a header file which is be used to configure some of 35 | * the application's compile time options, as an alternative to 36 | * specifying the compile time constants supplied through a 37 | * makefile or build system. 38 | * 39 | * For information on what each token does, refer to the 40 | * \ref Sec_Options section of the application documentation. 41 | */ 42 | 43 | #ifndef _APP_CONFIG_H_ 44 | #define _APP_CONFIG_H_ 45 | 46 | #define SECURE_MODE false 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /bootloader/Config/LUFAConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * \brief LUFA Library Configuration Header File 33 | * 34 | * This header file is used to configure LUFA's compile time options, 35 | * as an alternative to the compile time constants supplied through 36 | * a makefile. 37 | * 38 | * For information on what each token does, refer to the LUFA 39 | * manual section "Summary of Compile Tokens". 40 | */ 41 | 42 | #ifndef _LUFA_CONFIG_H_ 43 | #define _LUFA_CONFIG_H_ 44 | 45 | #if (ARCH == ARCH_AVR8) 46 | 47 | /* Non-USB Related Configuration Tokens: */ 48 | // #define DISABLE_TERMINAL_CODES 49 | 50 | /* USB Class Driver Related Tokens: */ 51 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 52 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 53 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 54 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 55 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 56 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 57 | // #define NO_CLASS_DRIVER_AUTOFLUSH 58 | 59 | /* General USB Driver Related Tokens: */ 60 | // #define ORDERED_EP_CONFIG 61 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) 62 | #define USB_DEVICE_ONLY 63 | // #define USB_HOST_ONLY 64 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 65 | // #define NO_LIMITED_CONTROLLER_CONNECT 66 | #define NO_SOF_EVENTS 67 | 68 | /* USB Device Mode Driver Related Tokens: */ 69 | #define USE_RAM_DESCRIPTORS 70 | // #define USE_FLASH_DESCRIPTORS 71 | // #define USE_EEPROM_DESCRIPTORS 72 | #define NO_INTERNAL_SERIAL 73 | #define FIXED_CONTROL_ENDPOINT_SIZE 32 74 | #define DEVICE_STATE_AS_GPIOR 0 75 | #define FIXED_NUM_CONFIGURATIONS 1 76 | #define CONTROL_ONLY_DEVICE 77 | // #define INTERRUPT_CONTROL_ENDPOINT 78 | #define NO_DEVICE_REMOTE_WAKEUP 79 | #define NO_DEVICE_SELF_POWER 80 | 81 | /* USB Host Mode Driver Related Tokens: */ 82 | // #define HOST_STATE_AS_GPIOR {Insert Value Here} 83 | // #define USB_HOST_TIMEOUT_MS {Insert Value Here} 84 | // #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} 85 | // #define NO_AUTO_VBUS_MANAGEMENT 86 | // #define INVERTED_VBUS_ENABLE_LINE 87 | 88 | #else 89 | 90 | #error Unsupported architecture for this LUFA configuration file. 91 | 92 | #endif 93 | #endif 94 | -------------------------------------------------------------------------------- /bootloader/Descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * USB Device Descriptors, for library use when in USB device mode. Descriptors are special 34 | * computer-readable structures which the host requests upon device enumeration, to determine 35 | * the device's capabilities and functions. 36 | */ 37 | 38 | #include "Descriptors.h" 39 | 40 | /** Device descriptor structure. This descriptor, located in SRAM memory, describes the overall 41 | * device characteristics, including the supported USB version, control endpoint size and the 42 | * number of device configurations. The descriptor is read out by the USB host when the enumeration 43 | * process begins. 44 | */ 45 | const USB_Descriptor_Device_t DeviceDescriptor = 46 | { 47 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 48 | 49 | .USBSpecification = VERSION_BCD(1,1,0), 50 | .Class = USB_CSCP_NoDeviceClass, 51 | .SubClass = USB_CSCP_NoDeviceSubclass, 52 | .Protocol = USB_CSCP_NoDeviceProtocol, 53 | 54 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 55 | 56 | .VendorID = 0x03EB, 57 | .ProductID = PRODUCT_ID_CODE, 58 | .ReleaseNumber = VERSION_BCD(0,0,0), 59 | 60 | .ManufacturerStrIndex = STRING_ID_Manufacturer, 61 | .ProductStrIndex = STRING_ID_Product, 62 | .SerialNumStrIndex = NO_DESCRIPTOR, 63 | 64 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 65 | }; 66 | 67 | /** Configuration descriptor structure. This descriptor, located in SRAM memory, describes the usage 68 | * of the device in one of its supported configurations, including information about any device interfaces 69 | * and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting 70 | * a configuration so that the host may correctly communicate with the USB device. 71 | */ 72 | const USB_Descriptor_Configuration_t ConfigurationDescriptor = 73 | { 74 | .Config = 75 | { 76 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 77 | 78 | .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), 79 | .TotalInterfaces = 1, 80 | 81 | .ConfigurationNumber = 1, 82 | .ConfigurationStrIndex = NO_DESCRIPTOR, 83 | 84 | .ConfigAttributes = USB_CONFIG_ATTR_RESERVED, 85 | 86 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) 87 | }, 88 | 89 | .DFU_Interface = 90 | { 91 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 92 | 93 | .InterfaceNumber = INTERFACE_ID_DFU, 94 | .AlternateSetting = 0, 95 | 96 | .TotalEndpoints = 0, 97 | 98 | .Class = 0xFE, 99 | .SubClass = 0x01, 100 | .Protocol = 0x02, 101 | 102 | .InterfaceStrIndex = NO_DESCRIPTOR 103 | }, 104 | 105 | .DFU_Functional = 106 | { 107 | .Header = {.Size = sizeof(USB_Descriptor_DFU_Functional_t), .Type = DTYPE_DFUFunctional}, 108 | 109 | .Attributes = (ATTR_CAN_UPLOAD | ATTR_CAN_DOWNLOAD), 110 | 111 | .DetachTimeout = 0x0000, 112 | .TransferSize = 0x0C00, 113 | 114 | .DFUSpecification = VERSION_BCD(1,1,0) 115 | } 116 | }; 117 | 118 | /** Language descriptor structure. This descriptor, located in SRAM memory, is returned when the host requests 119 | * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate 120 | * via the language ID table available at USB.org what languages the device supports for its string descriptors. 121 | */ 122 | const USB_Descriptor_String_t LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); 123 | 124 | /** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable 125 | * form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 126 | * Descriptor. 127 | */ 128 | const USB_Descriptor_String_t ManufacturerString = USB_STRING_DESCRIPTOR(L"System76"); 129 | 130 | /** Product descriptor string. This is a Unicode string containing the product's details in human readable form, 131 | * and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 132 | * Descriptor. 133 | */ 134 | const USB_Descriptor_String_t ProductString = USB_STRING_DESCRIPTOR(L"Io"); 135 | 136 | /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" 137 | * documentation) by the application code so that the address and size of a requested descriptor can be given 138 | * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function 139 | * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the 140 | * USB host. 141 | */ 142 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 143 | const uint16_t wIndex, 144 | const void** const DescriptorAddress) 145 | { 146 | const uint8_t DescriptorType = (wValue >> 8); 147 | const uint8_t DescriptorNumber = (wValue & 0xFF); 148 | 149 | const void* Address = NULL; 150 | uint16_t Size = NO_DESCRIPTOR; 151 | 152 | switch (DescriptorType) 153 | { 154 | case DTYPE_Device: 155 | Address = &DeviceDescriptor; 156 | Size = sizeof(USB_Descriptor_Device_t); 157 | break; 158 | case DTYPE_Configuration: 159 | Address = &ConfigurationDescriptor; 160 | Size = sizeof(USB_Descriptor_Configuration_t); 161 | break; 162 | case DTYPE_String: 163 | if (DescriptorNumber == STRING_ID_Language) 164 | { 165 | Address = &LanguageString; 166 | Size = LanguageString.Header.Size; 167 | } 168 | else if (DescriptorNumber == STRING_ID_Manufacturer) 169 | { 170 | Address = &ManufacturerString; 171 | Size = ManufacturerString.Header.Size; 172 | } 173 | else if (DescriptorNumber == STRING_ID_Product) 174 | { 175 | Address = &ProductString; 176 | Size = ProductString.Header.Size; 177 | } 178 | 179 | break; 180 | } 181 | 182 | *DescriptorAddress = Address; 183 | return Size; 184 | } 185 | -------------------------------------------------------------------------------- /bootloader/Descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for Descriptors.c. 34 | */ 35 | 36 | #ifndef _DESCRIPTORS_H_ 37 | #define _DESCRIPTORS_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | 42 | #include "Config/AppConfig.h" 43 | 44 | /* Macros: */ 45 | /** Descriptor type value for a DFU class functional descriptor. */ 46 | #define DTYPE_DFUFunctional 0x21 47 | 48 | /** DFU attribute mask, indicating that the DFU device will detach and re-attach when a DFU_DETACH 49 | * command is issued, rather than the host issuing a USB Reset. 50 | */ 51 | #define ATTR_WILL_DETATCH (1 << 3) 52 | 53 | /** DFU attribute mask, indicating that the DFU device can communicate during the manifestation phase 54 | * (memory programming phase). 55 | */ 56 | #define ATTR_MANEFESTATION_TOLLERANT (1 << 2) 57 | 58 | /** DFU attribute mask, indicating that the DFU device can accept DFU_UPLOAD requests to send data from 59 | * the device to the host. 60 | */ 61 | #define ATTR_CAN_UPLOAD (1 << 1) 62 | 63 | /** DFU attribute mask, indicating that the DFU device can accept DFU_DNLOAD requests to send data from 64 | * the host to the device. 65 | */ 66 | #define ATTR_CAN_DOWNLOAD (1 << 0) 67 | 68 | #if defined(__AVR_AT90USB1287__) 69 | #define PRODUCT_ID_CODE 0x2FFB 70 | #define AVR_SIGNATURE_1 0x1E 71 | #define AVR_SIGNATURE_2 0x97 72 | #define AVR_SIGNATURE_3 0x82 73 | #elif defined(__AVR_AT90USB647__) 74 | #define PRODUCT_ID_CODE 0x2FF9 75 | #define AVR_SIGNATURE_1 0x1E 76 | #define AVR_SIGNATURE_2 0x96 77 | #define AVR_SIGNATURE_3 0x82 78 | #elif defined(__AVR_AT90USB1286__) 79 | #define PRODUCT_ID_CODE 0x2FFB 80 | #define AVR_SIGNATURE_1 0x1E 81 | #define AVR_SIGNATURE_2 0x97 82 | #define AVR_SIGNATURE_3 0x82 83 | #elif defined(__AVR_AT90USB646__) 84 | #define PRODUCT_ID_CODE 0x2FF9 85 | #define AVR_SIGNATURE_1 0x1E 86 | #define AVR_SIGNATURE_2 0x96 87 | #define AVR_SIGNATURE_3 0x82 88 | #elif defined(__AVR_ATmega32U4__) 89 | #define PRODUCT_ID_CODE 0x2FF4 90 | #define AVR_SIGNATURE_1 0x1E 91 | #define AVR_SIGNATURE_2 0x95 92 | #define AVR_SIGNATURE_3 0x87 93 | #elif defined(__AVR_ATmega16U4__) 94 | #define PRODUCT_ID_CODE 0x2FF3 95 | #define AVR_SIGNATURE_1 0x1E 96 | #define AVR_SIGNATURE_2 0x94 97 | #define AVR_SIGNATURE_3 0x88 98 | #elif defined(__AVR_ATmega32U2__) 99 | #define PRODUCT_ID_CODE 0x2FF0 100 | #define AVR_SIGNATURE_1 0x1E 101 | #define AVR_SIGNATURE_2 0x95 102 | #define AVR_SIGNATURE_3 0x8A 103 | #elif defined(__AVR_ATmega16U2__) 104 | #define PRODUCT_ID_CODE 0x2FEF 105 | #define AVR_SIGNATURE_1 0x1E 106 | #define AVR_SIGNATURE_2 0x94 107 | #define AVR_SIGNATURE_3 0x89 108 | #elif defined(__AVR_AT90USB162__) 109 | #define PRODUCT_ID_CODE 0x2FFA 110 | #define AVR_SIGNATURE_1 0x1E 111 | #define AVR_SIGNATURE_2 0x94 112 | #define AVR_SIGNATURE_3 0x82 113 | #elif defined(__AVR_ATmega8U2__) 114 | #define PRODUCT_ID_CODE 0x2FEE 115 | #define AVR_SIGNATURE_1 0x1E 116 | #define AVR_SIGNATURE_2 0x93 117 | #define AVR_SIGNATURE_3 0x89 118 | #elif defined(__AVR_AT90USB82__) 119 | #define PRODUCT_ID_CODE 0x2FF7 120 | #define AVR_SIGNATURE_1 0x1E 121 | #define AVR_SIGNATURE_2 0x93 122 | #define AVR_SIGNATURE_3 0x82 123 | #else 124 | #error The selected AVR part is not currently supported by this bootloader. 125 | #endif 126 | 127 | #if !defined(PRODUCT_ID_CODE) 128 | #error Current AVR model is not supported by this bootloader. 129 | #endif 130 | 131 | /* Type Defines: */ 132 | /** Type define for a DFU class function descriptor. This descriptor gives DFU class information 133 | * to the host when read, indicating the DFU device's capabilities. 134 | */ 135 | typedef struct 136 | { 137 | USB_Descriptor_Header_t Header; /**< Standard descriptor header structure */ 138 | 139 | uint8_t Attributes; /**< DFU device attributes, a mask comprising of the 140 | * ATTR_* macros listed in this source file 141 | */ 142 | uint16_t DetachTimeout; /**< Timeout in milliseconds between a USB_DETACH 143 | * command being issued and the device detaching 144 | * from the USB bus 145 | */ 146 | uint16_t TransferSize; /**< Maximum number of bytes the DFU device can accept 147 | * from the host in a transaction 148 | */ 149 | uint16_t DFUSpecification; /**< BCD packed DFU specification number this DFU 150 | * device complies with 151 | */ 152 | } USB_Descriptor_DFU_Functional_t; 153 | 154 | /** Type define for the device configuration descriptor structure. This must be defined in the 155 | * application code, as the configuration descriptor contains several sub-descriptors which 156 | * vary between devices, and which describe the device's usage to the host. 157 | */ 158 | typedef struct 159 | { 160 | USB_Descriptor_Configuration_Header_t Config; 161 | 162 | // DFU Interface 163 | USB_Descriptor_Interface_t DFU_Interface; 164 | USB_Descriptor_DFU_Functional_t DFU_Functional; 165 | } USB_Descriptor_Configuration_t; 166 | 167 | /** Enum for the device interface descriptor IDs within the device. Each interface descriptor 168 | * should have a unique ID index associated with it, which can be used to refer to the 169 | * interface from other descriptors. 170 | */ 171 | enum InterfaceDescriptors_t 172 | { 173 | INTERFACE_ID_DFU = 0, /**< DFU interface descriptor ID */ 174 | }; 175 | 176 | /** Enum for the device string descriptor IDs within the device. Each string descriptor should 177 | * have a unique ID index associated with it, which can be used to refer to the string from 178 | * other descriptors. 179 | */ 180 | enum StringDescriptors_t 181 | { 182 | STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ 183 | STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ 184 | STRING_ID_Product = 2, /**< Product string ID */ 185 | }; 186 | 187 | /* Function Prototypes: */ 188 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 189 | const uint16_t wIndex, 190 | const void** const DescriptorAddress) 191 | ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 192 | 193 | #endif 194 | 195 | -------------------------------------------------------------------------------- /bootloader/asf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | DFU Class Bootloader, capable of reprogramming a device using the Atmel FLIP or other AVR DFU programming software when plugged into a host. 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /bootloader/makefile: -------------------------------------------------------------------------------- 1 | # 2 | # LUFA Library 3 | # Copyright (C) Dean Camera, 2018. 4 | # 5 | # dean [at] fourwalledcubicle [dot] com 6 | # www.lufa-lib.org 7 | # 8 | # -------------------------------------- 9 | # LUFA Project Makefile. 10 | # -------------------------------------- 11 | 12 | # Run "make help" for target help. 13 | 14 | MCU = atmega32u4 15 | ARCH = AVR8 16 | BOARD = NONE 17 | F_CPU = 16000000 18 | F_USB = $(F_CPU) 19 | OPTIMIZATION = s 20 | TARGET = BootloaderDFU 21 | SRC = $(TARGET).c Descriptors.c BootloaderAPI.c BootloaderAPITable.S $(LUFA_SRC_USB) 22 | LUFA_PATH = ../lufa/LUFA 23 | CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/ -DBOOT_START_ADDR=$(BOOT_START_OFFSET) 24 | LD_FLAGS = -Wl,--section-start=.text=$(BOOT_START_OFFSET) $(BOOT_API_LD_FLAGS) 25 | 26 | # Flash size and bootloader section sizes of the target, in KB. These must 27 | # match the target's total FLASH size and the bootloader size set in the 28 | # device's fuses. 29 | FLASH_SIZE_KB = 32 30 | BOOT_SECTION_SIZE_KB = 4 31 | 32 | # Bootloader address calculation formulas 33 | # Do not modify these macros, but rather modify the dependent values above. 34 | CALC_ADDRESS_IN_HEX = $(shell printf "0x%X" $$(( $(1) )) ) 35 | BOOT_START_OFFSET = $(call CALC_ADDRESS_IN_HEX, ($(FLASH_SIZE_KB) - $(BOOT_SECTION_SIZE_KB)) * 1024 ) 36 | BOOT_SEC_OFFSET = $(call CALC_ADDRESS_IN_HEX, ($(FLASH_SIZE_KB) * 1024) - ($(strip $(1))) ) 37 | 38 | # Bootloader linker section flags for relocating the API table sections to 39 | # known FLASH addresses - these should not normally be user-edited. 40 | BOOT_SECTION_LD_FLAG = -Wl,--section-start=$(strip $(1))=$(call BOOT_SEC_OFFSET, $(3)) -Wl,--undefined=$(strip $(2)) 41 | BOOT_API_LD_FLAGS = $(call BOOT_SECTION_LD_FLAG, .apitable_trampolines, BootloaderAPI_Trampolines, 96) 42 | BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, .apitable_jumptable, BootloaderAPI_JumpTable, 32) 43 | BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, .apitable_signatures, BootloaderAPI_Signatures, 8) 44 | 45 | # Default target 46 | all: 47 | 48 | # Include LUFA-specific DMBS extension modules 49 | DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA 50 | include $(DMBS_LUFA_PATH)/lufa-sources.mk 51 | include $(DMBS_LUFA_PATH)/lufa-gcc.mk 52 | 53 | # Include common DMBS build system modules 54 | DMBS_PATH ?= $(LUFA_PATH)/Build/DMBS/DMBS 55 | include $(DMBS_PATH)/core.mk 56 | include $(DMBS_PATH)/cppcheck.mk 57 | include $(DMBS_PATH)/doxygen.mk 58 | include $(DMBS_PATH)/dfu.mk 59 | include $(DMBS_PATH)/gcc.mk 60 | include $(DMBS_PATH)/hid.mk 61 | include $(DMBS_PATH)/avrdude.mk 62 | include $(DMBS_PATH)/atprogram.mk 63 | -------------------------------------------------------------------------------- /buildchain.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thelio-io-firmware", 3 | "base": "ubuntu:16.04", 4 | "prepare": [ 5 | ["apt-get", "update"], 6 | ["apt-get", "dist-upgrade", "-y"], 7 | ["apt-get", "install", "-y", "avr-libc", "build-essential"] 8 | ], 9 | "build": [ 10 | ["make", "-C", "source"] 11 | ], 12 | "publish": [ 13 | ["cp", "-v", "source/build/atmega32u4/boot.hex", "artifacts"], 14 | ["cp", "-v", "source/build/atmega32u4/main.hex", "artifacts"], 15 | ["cp", "-v", "source/build/atmega32u4/metadata.json", "artifacts"] 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /mk/atmega1280.mk: -------------------------------------------------------------------------------- 1 | PORT=/dev/ttyUSB0 2 | CONSOLE_BAUD=57600 3 | PROGRAMMER=arduino 4 | PROGRAMMER_BAUD=57600 5 | -------------------------------------------------------------------------------- /mk/atmega2560.mk: -------------------------------------------------------------------------------- 1 | PORT=/dev/ttyACM0 2 | CONSOLE_BAUD=57600 3 | PROGRAMMER=wiring 4 | PROGRAMMER_BAUD=115200 5 | -------------------------------------------------------------------------------- /mk/atmega32u4.mk: -------------------------------------------------------------------------------- 1 | PORT=/dev/ttyACM0 2 | CONSOLE_BAUD=57600 3 | PROGRAMMER=avr109 4 | PROGRAMMER_BAUD=115200 5 | -------------------------------------------------------------------------------- /scripts/add_dfu_header.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2020 Richard Hughes 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1+ 7 | 8 | import struct 9 | import zlib 10 | import argparse 11 | 12 | 13 | def main(bin_fn, dfu_fn, pad, vid, pid, rev): 14 | 15 | # read binary file 16 | with open(bin_fn, 'rb') as f: 17 | blob = f.read() 18 | 19 | # pad blob to a specific size 20 | if pad: 21 | while len(blob) < int(pad, 16): 22 | blob += b'\0' 23 | 24 | # create DFU footer with checksum 25 | blob += struct.pack( 26 | '&2 32 | exit 1 33 | fi 34 | DESCRIPTION="$1" 35 | echo "DESCRIPTION: ${DESCRIPTION}" 36 | 37 | NAME="thelio-io_${REVISION}" 38 | echo "NAME: ${NAME}" 39 | 40 | SOURCE="https://github.com/system76/thelio-io-firmware/releases/tag/${REVISION}" 41 | echo "SOURCE: ${SOURCE}" 42 | 43 | BUILD="build/lvfs/${NAME}" 44 | echo "BUILD: ${BUILD}" 45 | 46 | rm -rf "${BUILD}" 47 | mkdir -pv "${BUILD}" 48 | 49 | make "build/atmega32u4/main.bin" 50 | ./scripts/add_dfu_header.py \ 51 | --bin "build/atmega32u4/main.bin" \ 52 | --dfu "${BUILD}/firmware.dfu" \ 53 | --vid "${RUNTIME_VID}" \ 54 | --pid "${RUNTIME_PID}" \ 55 | --rev "${RUNTIME_REV}" 56 | 57 | echo "writing '${BUILD}/firmware.metainfo.xml'" 58 | cat > "${BUILD}/firmware.metainfo.xml" < 60 | 61 | 62 | com.system76.ThelioIo.firmware 63 | Thelio Io 64 | Firmware for the System76 Thelio Io 65 | 66 |

67 | The System76 Thelio Io firmware provides fan and power button control 68 | for System76 Thelio systems. There is additional drive backplane 69 | functionality that is not controlled by firmware. 70 |

71 |
72 | 73 | 74 | ${RUNTIME_UUID} 75 | 76 | https://github.com/system76/thelio-io 77 | CC0-1.0 78 | GPL-3.0+ 79 | System76 80 | 81 | 82 | 83 | ${SOURCE} 84 | 85 |

${DESCRIPTION}

86 |
87 |
88 |
89 | 90 | X-Device 91 | 92 | 93 | dfu 94 | 95 | 96 | org.usb.dfu 97 | triplet 98 | 99 |
100 | EOF 101 | 102 | gcab \ 103 | --verbose \ 104 | --create \ 105 | --nopath \ 106 | "${BUILD}.cab" \ 107 | "${BUILD}/"* 108 | 109 | echo "created '${BUILD}.cab'" 110 | -------------------------------------------------------------------------------- /src/battery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpu.h" 4 | #include 5 | #include "battery.h" 6 | #include "i2c/master.h" 7 | #include "i2c/slave.h" 8 | 9 | uint8_t i2c_get_16(uint8_t chip, uint8_t command, uint16_t * value) { 10 | uint8_t status = i2c_get(chip, command, (uint8_t *)value, 2); 11 | if (status != 0) { 12 | return status; 13 | } 14 | 15 | return 0; 16 | } 17 | 18 | struct Battery battery_new(uint8_t address) { 19 | struct Battery battery = { 20 | .address = address 21 | }; 22 | 23 | return battery; 24 | } 25 | 26 | uint8_t battery_mode(struct Battery * battery, uint16_t * value) { 27 | return i2c_get_16(battery->address, 0x03, value); 28 | } 29 | 30 | uint8_t battery_temp(struct Battery * battery, uint16_t * value) { 31 | return i2c_get_16(battery->address, 0x08, value); 32 | } 33 | 34 | uint8_t battery_voltage(struct Battery * battery, uint16_t * value) { 35 | return i2c_get_16(battery->address, 0x09, value); 36 | } 37 | 38 | uint8_t battery_current(struct Battery * battery, uint16_t * value) { 39 | return i2c_get_16(battery->address, 0x0A, value); 40 | } 41 | 42 | uint8_t battery_avg_current(struct Battery * battery, uint16_t * value) { 43 | return i2c_get_16(battery->address, 0x0B, value); 44 | } 45 | 46 | uint8_t battery_charge(struct Battery * battery, uint16_t * value) { 47 | return i2c_get_16(battery->address, 0x0D, value); 48 | } 49 | 50 | uint8_t battery_abs_charge(struct Battery * battery, uint16_t * value) { 51 | return i2c_get_16(battery->address, 0x0E, value); 52 | } 53 | 54 | uint8_t battery_capacity(struct Battery * battery, uint16_t * value) { 55 | return i2c_get_16(battery->address, 0x0F, value); 56 | } 57 | 58 | uint8_t battery_full_capacity(struct Battery * battery, uint16_t * value) { 59 | return i2c_get_16(battery->address, 0x10, value); 60 | } 61 | 62 | uint8_t battery_status(struct Battery * battery, uint16_t * value) { 63 | return i2c_get_16(battery->address, 0x16, value); 64 | } 65 | 66 | uint8_t battery_cycle_count(struct Battery * battery, uint16_t * value) { 67 | return i2c_get_16(battery->address, 0x17, value); 68 | } 69 | 70 | uint8_t battery_design_capacity(struct Battery * battery, uint16_t * value) { 71 | return i2c_get_16(battery->address, 0x18, value); 72 | } 73 | 74 | uint8_t battery_design_voltage(struct Battery * battery, uint16_t * value) { 75 | return i2c_get_16(battery->address, 0x19, value); 76 | } 77 | 78 | uint8_t battery_date(struct Battery * battery, uint16_t * value) { 79 | return i2c_get_16(battery->address, 0x1B, value); 80 | } 81 | 82 | uint8_t battery_serial(struct Battery * battery, uint16_t * value) { 83 | return i2c_get_16(battery->address, 0x1C, value); 84 | } 85 | 86 | uint8_t battery_oem(struct Battery * battery, uint8_t * buf, int len) { 87 | if (len < 1) { 88 | printf("%d < 1\n", len); 89 | return 1; 90 | } 91 | 92 | uint8_t status = i2c_get(battery->address, 0x20, buf, 1); 93 | if (status != 0) { 94 | return status; 95 | } 96 | 97 | if (buf[0] >= len) { 98 | printf("%u >= %d\n", buf[0], len); 99 | return 2; 100 | } 101 | 102 | _delay_ms(1); 103 | 104 | return i2c_get(battery->address, 0x20, buf, buf[0] + 1); 105 | } 106 | 107 | uint8_t battery_model(struct Battery * battery, uint8_t * buf, int len) { 108 | if (len < 1) { 109 | printf("%d < 1\n", len); 110 | return 1; 111 | } 112 | 113 | uint8_t status = i2c_get(battery->address, 0x21, buf, 1); 114 | if (status != 0) { 115 | return status; 116 | } 117 | 118 | if (buf[0] >= len) { 119 | printf("%u >= %d\n", buf[0], len); 120 | return 2; 121 | } 122 | 123 | _delay_ms(1); 124 | 125 | return i2c_get(battery->address, 0x21, buf, buf[0] + 1); 126 | } 127 | 128 | uint8_t battery_technology(struct Battery * battery, uint8_t * buf, int len) { 129 | if (len < 1) { 130 | printf("%d < 1\n", len); 131 | return 1; 132 | } 133 | 134 | uint8_t status = i2c_get(battery->address, 0x22, buf, 1); 135 | if (status != 0) { 136 | return status; 137 | } 138 | 139 | if (buf[0] >= len) { 140 | printf("%u >= %d\n", buf[0], len); 141 | return 2; 142 | } 143 | 144 | _delay_ms(1); 145 | 146 | return i2c_get(battery->address, 0x22, buf, buf[0] + 1); 147 | } 148 | -------------------------------------------------------------------------------- /src/battery.h: -------------------------------------------------------------------------------- 1 | #ifndef BATTERY_H 2 | #define BATTERY_H 3 | 4 | struct Battery { 5 | uint8_t address; 6 | }; 7 | 8 | struct Battery battery_new(uint8_t address); 9 | 10 | uint8_t battery_mode(struct Battery * battery, uint16_t * value); 11 | uint8_t battery_temp(struct Battery * battery, uint16_t * value); 12 | uint8_t battery_voltage(struct Battery * battery, uint16_t * value); 13 | uint8_t battery_current(struct Battery * battery, uint16_t * value); 14 | uint8_t battery_avg_current(struct Battery * battery, uint16_t * value); 15 | uint8_t battery_charge(struct Battery * battery, uint16_t * value); 16 | uint8_t battery_abs_charge(struct Battery * battery, uint16_t * value); 17 | uint8_t battery_capacity(struct Battery * battery, uint16_t * value); 18 | uint8_t battery_full_capacity(struct Battery * battery, uint16_t * value); 19 | uint8_t battery_status(struct Battery * battery, uint16_t * value); 20 | uint8_t battery_cycle_count(struct Battery * battery, uint16_t * value); 21 | uint8_t battery_design_capacity(struct Battery * battery, uint16_t * value); 22 | uint8_t battery_design_voltage(struct Battery * battery, uint16_t * value); 23 | uint8_t battery_date(struct Battery * battery, uint16_t * value); 24 | uint8_t battery_serial(struct Battery * battery, uint16_t * value); 25 | uint8_t battery_oem(struct Battery * battery, uint8_t * buf, int len); 26 | uint8_t battery_model(struct Battery * battery, uint8_t * buf, int len); 27 | uint8_t battery_technology(struct Battery * battery, uint8_t * buf, int len); 28 | 29 | #endif // BATTERY_H 30 | -------------------------------------------------------------------------------- /src/cmd/console.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../stdio.h" 4 | #include "../uart.h" 5 | 6 | int cmd_console(int argc, char ** argv) { 7 | if (argc > 1) { 8 | int num = atoi(argv[1]); 9 | if(num < 0) { 10 | printf("invalid console '%d' from input '%s' not in %d..%d\n", num, argv[1], 0, uart_count()); 11 | return 1; 12 | } 13 | 14 | struct Uart * uart = uart_new(num); 15 | if (uart == NULL) { 16 | printf("console '%d' not found\n", num); 17 | return 1; 18 | } 19 | if (uart == stdio_uart) { 20 | printf("console '%d' is currently stdio\n", num); 21 | return 1; 22 | } 23 | 24 | uart_init(uart, 57600); 25 | while (uart_can_read(uart)) { 26 | uart_read(uart); 27 | } 28 | uart_write(uart, 3); 29 | 30 | while (1) { 31 | if (uart_can_read(uart)) { 32 | char c = uart_read(uart); 33 | if (c == 3) { 34 | return 0; 35 | } 36 | uart_write(stdio_uart, c); 37 | } 38 | if (uart_can_read(stdio_uart)) { 39 | char c = uart_read(stdio_uart); 40 | if (c == 3) { 41 | return 0; 42 | } 43 | uart_write(uart, c); 44 | } 45 | } 46 | } else { 47 | printf("no console specified\n"); 48 | return 1; 49 | } 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /src/cmd/driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../cpu.h" 4 | #include 5 | #include "../battery.h" 6 | #include "../keyboard/mod.h" 7 | #include "../macro.h" 8 | #include "../pin.h" 9 | #include "../stdio.h" 10 | #include "../uart.h" 11 | 12 | int cmd_driver(int argc, char ** argv) { 13 | printf("Running driver. CTRL-C to exit\n"); 14 | 15 | struct Pin * pins[KEY_PINS] = { NULL }; 16 | // 276 = 1 + 2 + 3 ... + 23 17 | unsigned char map[512] = { 0 }; 18 | int map_i; 19 | 20 | // Initialize pins 21 | int i; 22 | for(i = 0; i < KEY_PINS; i++) { 23 | struct Pin * pin = pin_new(key_pins[i]); 24 | 25 | // Verify pins match names 26 | if (strcmp(pin->name, key_pin_names[i]) != 0){ 27 | printf("Key pin %d: %s (%d) does not match %s\n", i, pin->name, key_pins[i], key_pin_names[i]); 28 | return 1; 29 | } 30 | 31 | // Set all pins to inputs with pull-ups 32 | pin_set_dir(pin, 0); 33 | pin_set(pin, 1); 34 | 35 | pins[i] = pin; 36 | } 37 | 38 | struct Battery battery = battery_new(0x0B); 39 | 40 | uint16_t bat_data; 41 | int bat_i = 0; 42 | 43 | struct Uart * uart = stdio_uart; 44 | 45 | while(1) { 46 | // Iterate each pin, setting it to ground, then check for affected pins 47 | map_i = 0; 48 | for(i = 0; i < KEY_PINS - 1; i++) { 49 | struct Pin * pin = pins[i]; 50 | 51 | // Set to output, ground 52 | pin_set_dir(pin, 1); 53 | pin_set(pin, 0); 54 | 55 | _delay_us(1); 56 | 57 | // Iterate all other pins, checking for grounding inputs 58 | int j; 59 | for(j = i + 1; j < KEY_PINS; j++) { 60 | struct Pin * other = pins[j]; 61 | 62 | if (map_i >= sizeof(map)) { 63 | printf("Map index %d is out of range 0..%d", map_i, sizeof(map)); 64 | return 1; 65 | } 66 | 67 | // Check if pin is grounded 68 | if(pin_get(other) == 0) { 69 | if (map[map_i]) { 70 | //printf("%d hold\n", map_i); 71 | map[map_i] = 2; 72 | } else { 73 | //printf("k %s 1\n", key_name(i, j)); 74 | map[map_i] = 1; 75 | } 76 | } else if (map[map_i]) { 77 | //printf("k %s 0\n", key_name(i, j)); 78 | map[map_i] = 0; 79 | } 80 | 81 | map_i += 1; 82 | } 83 | 84 | // Reset to input with pull-up 85 | pin_set_dir(pin, 0); 86 | pin_set(pin, 1); 87 | } 88 | 89 | if (bat_i % 1024 == 0) { 90 | bat_i = 0; 91 | 92 | #define BAT_PROP(N) \ 93 | if(battery_ ## N(&battery, &bat_data) == 0) { \ 94 | printf("b %s %d\n", str(N), bat_data); \ 95 | } 96 | 97 | BAT_PROP(temp) 98 | 99 | BAT_PROP(voltage) 100 | BAT_PROP(current) 101 | 102 | BAT_PROP(capacity) 103 | BAT_PROP(full_capacity) 104 | 105 | BAT_PROP(cycle_count) 106 | 107 | BAT_PROP(design_capacity) 108 | BAT_PROP(design_voltage) 109 | } 110 | 111 | bat_i++; 112 | 113 | if (uart_can_read(uart)) { 114 | char c = uart_read(uart); 115 | if (c == 3) { 116 | break; 117 | } 118 | } 119 | } 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /src/cmd/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int cmd_echo(int argc, char ** argv) { 4 | int i; 5 | for (i = 1; i < argc; i++) { 6 | if (i > 1) { 7 | printf(" "); 8 | } 9 | printf("%s", argv[i]); 10 | } 11 | printf("\n"); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/cmd/eeprom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef __AVR_ATmega32U4__ 7 | #define EEPROM_SIZE 1024 8 | #else 9 | #error "Could not find eeprom definitions" 10 | #endif 11 | 12 | int cmd_eeprom(int argc, char ** argv) { 13 | if (argc > 1) { 14 | errno = 0; 15 | unsigned long address = strtoul(argv[1], NULL, 16); 16 | if (errno) { 17 | printf("address '%s' invalid\n", argv[1]); 18 | return 1; 19 | } 20 | if (address >= EEPROM_SIZE) { 21 | printf("address '%s' not in %x..%x\n", argv[1], 0, EEPROM_SIZE - 1); 22 | return 1; 23 | } 24 | 25 | uint8_t * ptr = (uint8_t *)((uint16_t)(address)); 26 | if (argc > 2) { 27 | errno = 0; 28 | unsigned long value = strtoul(argv[2], NULL, 16); 29 | if (errno) { 30 | printf("value '%s' invalid\n", argv[2]); 31 | return 1; 32 | } 33 | if (value > 0xFF) { 34 | printf("value '%s' not in %d..%d\n", argv[2], 0, 0xFF); 35 | return 1; 36 | } 37 | 38 | eeprom_update_byte(ptr, (uint8_t)value); 39 | } else { 40 | uint8_t value = eeprom_read_byte(ptr); 41 | printf("%02X\n", value); 42 | } 43 | } else { 44 | uint16_t address; 45 | for (address = 0; address < EEPROM_SIZE; address++) { 46 | if (address % 16 == 0) { 47 | if (address != 0) { 48 | printf("\n"); 49 | } 50 | printf("%04X:", address); 51 | } 52 | 53 | printf(" %02X", eeprom_read_byte((uint8_t *)address)); 54 | } 55 | 56 | if (address > 0) { 57 | printf("\n"); 58 | } 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /src/cmd/exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int cmd_exit(int argc, char ** argv) { 4 | putchar(3); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /src/cmd/help.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mod.h" 3 | 4 | int cmd_help(int argc, char ** argv) { 5 | printf("Commands\n"); 6 | int i; 7 | for (i = 0; ; i++) { 8 | char * name = commands[i].name; 9 | if(name == NULL) { 10 | break; 11 | } 12 | printf(" %s\n", commands[i].name); 13 | } 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/cmd/i2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../i2c/master.h" 4 | 5 | #define MIN_CHIP 0x03 6 | #define MAX_CHIP 0x77 7 | 8 | #define MIN_DATA 0x00 9 | #define MAX_DATA 0xFF 10 | 11 | #define MIN_VALUE 0x00 12 | #define MAX_VALUE 0xFF 13 | 14 | int cmd_i2c(int argc, char ** argv) { 15 | if (argc > 1) { 16 | int chip = strtol(argv[1], NULL, 16); 17 | if (chip < MIN_CHIP || chip > MAX_CHIP) { 18 | printf("chip address '0x%X' from input '%s' not in 0x%X..0x%X\n", (unsigned int)chip, argv[1], MIN_CHIP, MAX_CHIP); 19 | return 1; 20 | } 21 | 22 | if (argc > 2) { 23 | int data = strtol(argv[2], NULL, 16); 24 | if (data < MIN_DATA || data > MAX_DATA) { 25 | printf("data address '0x%X' from input '%s' not in 0x%X..0x%X\n", (unsigned int)data, argv[2], MIN_DATA, MAX_DATA); 26 | return 1; 27 | } 28 | 29 | if (argc > 3) { 30 | int value = strtol(argv[3], NULL, 16); 31 | if (value < MIN_VALUE || value > MAX_VALUE){ 32 | printf("value '0x%X' from input '%s' not in 0x%X..0x%X\n", (unsigned int)value, argv[3], MIN_VALUE, MAX_VALUE); 33 | return 1; 34 | } 35 | 36 | printf("set 0x%X 0x%X 0x%X\n", (unsigned int)chip, (unsigned int)data, (unsigned int)value); 37 | 38 | uint8_t buf[1] = { (uint8_t)value }; 39 | uint8_t status = i2c_set(chip, data, buf, 1); 40 | if(status != 0) { 41 | printf("error %d\n", status); 42 | return 1; 43 | } 44 | } else { 45 | printf("get 0x%X 0x%X\n", (unsigned int)chip, (unsigned int)data); 46 | 47 | uint8_t buf[1] = { 0 }; 48 | uint8_t status = i2c_get(chip, data, buf, 1); 49 | if(status != 0) { 50 | printf("error %d\n", status); 51 | return 1; 52 | } 53 | 54 | printf("= 0x%X\n", buf[0]); 55 | } 56 | } else { 57 | printf("no data address provided in 0x%X..0x%X\n", MIN_DATA, MAX_DATA); 58 | return 1; 59 | } 60 | } else { 61 | printf("no chip address provided in 0x%X..0x%X\n", MIN_CHIP, MAX_CHIP); 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /src/cmd/i2c_slave.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../i2c/slave.h" 6 | 7 | #define MIN_CHIP 0x03 8 | #define MAX_CHIP 0x77 9 | 10 | void cmd_i2c_slave_new() { 11 | printf("New transaction\n"); 12 | } 13 | 14 | void cmd_i2c_slave_recv(uint8_t data) { 15 | printf("Got 0x%X\n", (unsigned int)data); 16 | } 17 | 18 | uint8_t cmd_i2c_slave_send() { 19 | uint8_t data = 0x42; 20 | printf("Sending 0x%X\n", data); 21 | return data; 22 | } 23 | 24 | int cmd_i2c_slave(int argc, char ** argv) { 25 | if (argc > 1) { 26 | if (strcmp(argv[1], "on") == 0) { 27 | if (argc > 2) { 28 | int chip = strtol(argv[2], NULL, 16); 29 | if (chip < MIN_CHIP || chip > MAX_CHIP) { 30 | printf("chip address '0x%X' from input '%s' not in 0x%X..0x%X\n", (unsigned int)chip, argv[2], MIN_CHIP, MAX_CHIP); 31 | return 1; 32 | } 33 | 34 | printf("entering slave mode for address 0x%X\n", (unsigned int)chip); 35 | 36 | i2c_slave_init(chip, cmd_i2c_slave_new, cmd_i2c_slave_recv, cmd_i2c_slave_send); 37 | } else { 38 | printf("no chip address provided in 0x%X..0x%X\n", MIN_CHIP, MAX_CHIP); 39 | return 1; 40 | } 41 | } else if (strcmp(argv[1], "off") == 0) { 42 | printf("exiting slave mode\n"); 43 | 44 | i2c_slave_stop(); 45 | } else { 46 | printf("must specify either on or off\n"); 47 | return 1; 48 | } 49 | } else if (TWCR & (1<> 1); 51 | } else { 52 | printf("not running in slave mode\n"); 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /src/cmd/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../cpu.h" 4 | #include 5 | #include "../keyboard/hid.h" 6 | #include "../keyboard/mod.h" 7 | #include "../pin.h" 8 | #include "../stdio.h" 9 | #include "../uart.h" 10 | 11 | #define MAX_KEYS 7 12 | 13 | int cmd_keyboard(int argc, char ** argv) { 14 | printf("reading keypresses. CTRL-C to exit\n"); 15 | 16 | struct Uart * uart = stdio_uart; 17 | 18 | struct Uart * hid_uart = uart_new(0); 19 | uart_init(hid_uart, 9600); 20 | uint8_t hid_report; 21 | uint16_t hid; 22 | int hid_i; 23 | 24 | struct Pin * pins[KEY_PINS] = { NULL }; 25 | int i; 26 | int j; 27 | 28 | struct Key * keys[MAX_KEYS] = { NULL }; 29 | struct Key * key; 30 | int key_i = 0; 31 | 32 | unsigned char fn_key = 0; 33 | 34 | // Initialize pins 35 | for(i = 0; i < KEY_PINS; i++) { 36 | struct Pin * pin = pin_new(key_pins[i]); 37 | 38 | // Verify pins match names 39 | if (strcmp(pin->name, key_pin_names[i]) != 0){ 40 | printf("Key pin %d: %s (%d) does not match %s\n", i, pin->name, key_pins[i], key_pin_names[i]); 41 | return 1; 42 | } 43 | 44 | // Set all pins to inputs with pull-ups 45 | pin_set_dir(pin, 0); 46 | pin_set(pin, 1); 47 | 48 | pins[i] = pin; 49 | } 50 | 51 | while(1) { 52 | for(key_i = 0; key_i < MAX_KEYS; key_i++) { 53 | keys[key_i] = NULL; 54 | } 55 | key_i = 0; 56 | fn_key = 0; 57 | 58 | // Iterate each pin, setting it to ground, then check for affected pins 59 | for(i = 0; i < KEY_PINS - 1; i++) { 60 | struct Pin * pin = pins[i]; 61 | 62 | // Set to output, ground 63 | pin_set_dir(pin, 1); 64 | pin_set(pin, 0); 65 | 66 | _delay_us(1); 67 | 68 | // Iterate all other pins, checking for grounding inputs 69 | for(j = i + 1; j < KEY_PINS; j++) { 70 | struct Pin * other = pins[j]; 71 | 72 | // Check if pin is grounded 73 | if(pin_get(other) == 0) { 74 | key = key_new(i, j); 75 | if (key != NULL) { 76 | printf("%s\n", key->name ? key->name : "UNKNOWN"); 77 | if (key->hid == KEY_FN) { 78 | // Function key 79 | fn_key = 1; 80 | } else if (key_i < sizeof(keys)) { 81 | keys[key_i] = key; 82 | key_i++; 83 | } 84 | } else { 85 | printf("%d, %d key not found\n", i, j); 86 | } 87 | } 88 | } 89 | 90 | // Reset to input with pull-up 91 | pin_set_dir(pin, 0); 92 | pin_set(pin, 1); 93 | } 94 | 95 | // Evaluate keys for each HID report 96 | for(hid_report = 0; hid_report < 3; hid_report++) { 97 | // Write the report number 98 | uart_write(hid_uart, hid_report + 1); 99 | 100 | hid_i = 0; 101 | 102 | // Write each key that is of the correct hid type 103 | for(key_i = 0; key_i < MAX_KEYS; key_i++) { 104 | hid = 0; 105 | 106 | key = keys[key_i]; 107 | if (key != NULL) { 108 | hid = fn_key ? key->fn_hid : key->hid; 109 | } 110 | 111 | if (hid != 0 && (uint8_t)(hid >> 8) == hid_report) { 112 | printf("HID %d, %d: %X\n", hid_report + 1, hid_i, (uint8_t)hid); 113 | uart_write(hid_uart, (uint8_t)hid); 114 | hid_i ++; 115 | } 116 | } 117 | 118 | // Write the remainder of the descriptor 119 | for(; hid_i < MAX_KEYS; hid_i++) { 120 | uart_write(hid_uart, 0); 121 | } 122 | } 123 | 124 | // Exit if ctrl-c is pressed 125 | if (uart_can_read(uart)) { 126 | char c = uart_read(uart); 127 | if (c == 3) { 128 | break; 129 | } 130 | } 131 | } 132 | 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /src/cmd/mod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mod.h" 5 | #include "../tokenize.h" 6 | 7 | #undef CMD 8 | #define CMD(N) int cmd_ ## N(int argc, char ** argv); 9 | 10 | CMDS 11 | 12 | #undef CMD 13 | #define CMD(N) { #N, cmd_ ## N }, 14 | 15 | struct command commands[] = { 16 | CMDS 17 | { NULL, NULL } 18 | }; 19 | 20 | void run(char * line) { 21 | int line_len = strlen(line); 22 | char * line_copy = malloc(line_len); 23 | if (line_copy == NULL) { 24 | printf("run: failed to allocate\n"); 25 | return; 26 | } 27 | 28 | strcpy(line_copy, line); 29 | 30 | char * cmds[8] = { NULL }; 31 | int cmds_len = tokenize(';', line_copy, line_len, cmds, 7); 32 | cmds[cmds_len] = NULL; 33 | 34 | int cmd_i; 35 | for(cmd_i = 0; cmd_i < cmds_len; cmd_i++) { 36 | char * cmd = cmds[cmd_i]; 37 | int cmd_len = strlen(cmd); 38 | 39 | char * argv[8] = { NULL }; 40 | int argc = tokenize(' ', cmd, cmd_len, argv, 7); 41 | 42 | if (argc > 0) { 43 | int i; 44 | for (i = 0; ; i++) { 45 | char * name = commands[i].name; 46 | if(name == NULL) { 47 | printf("unknown command: '%s'\n", argv[0]); 48 | break; 49 | } 50 | if (strcmp(argv[0], name) == 0) { 51 | commands[i].func(argc, argv); 52 | break; 53 | } 54 | } 55 | } 56 | } 57 | 58 | free(line_copy); 59 | } 60 | -------------------------------------------------------------------------------- /src/cmd/mod.h: -------------------------------------------------------------------------------- 1 | #define CMD(N) N 2 | 3 | #define CMDS \ 4 | CMD(battery) \ 5 | CMD(console) \ 6 | CMD(driver) \ 7 | CMD(echo) \ 8 | CMD(eeprom) \ 9 | CMD(exit) \ 10 | CMD(help) \ 11 | CMD(i2c) \ 12 | CMD(i2c_slave) \ 13 | CMD(keyboard) \ 14 | CMD(pin) \ 15 | CMD(pwm) \ 16 | CMD(sleep) \ 17 | CMD(thelio) \ 18 | CMD(time) 19 | 20 | struct command { 21 | char * name; 22 | int (*func)(int argc, char ** argv); 23 | }; 24 | 25 | extern struct command commands[]; 26 | 27 | void run(char * line); 28 | -------------------------------------------------------------------------------- /src/cmd/pin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../pin.h" 5 | 6 | int cmd_pin(int argc, char ** argv) { 7 | if (argc > 1) { 8 | struct Pin * pin = pin_from_name(argv[1]); 9 | 10 | if (pin == NULL) { 11 | printf("pin '%s' not found\n", argv[1]); 12 | return 1; 13 | } 14 | 15 | if(argc > 2) { 16 | // Set direction to output 17 | pin_set_dir(pin, 1); 18 | 19 | if(strcmp(argv[2], "on") == 0) { 20 | printf("setting pin %s to on\n", pin->name); 21 | pin_set(pin, 1); 22 | } else if (strcmp(argv[2], "off") == 0) { 23 | printf("setting pin %s to off\n", pin->name); 24 | pin_set(pin, 0); 25 | } else { 26 | printf("provide on or off to set\n"); 27 | return 1; 28 | } 29 | } else { 30 | // Set direction to input 31 | pin_set_dir(pin, 0); 32 | 33 | // Disable pull-up resistor 34 | pin_set(pin, 0); 35 | 36 | if (pin_get(pin)) { 37 | printf("getting pin %s: on\n", pin->name); 38 | } else { 39 | printf("getting pin %s: off\n", pin->name); 40 | } 41 | } 42 | } else { 43 | printf("no pin provided\n"); 44 | return 1; 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/cmd/pwm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../pin.h" 5 | #include "../stdio.h" 6 | #include "../uart.h" 7 | 8 | int cmd_pwm(int argc, char ** argv) { 9 | if (argc > 1) { 10 | struct Pin * pin = pin_from_name(argv[1]); 11 | 12 | if (pin == NULL) { 13 | printf("pin '%s' not found\n", argv[1]); 14 | return 1; 15 | } 16 | 17 | if(argc > 2) { 18 | // Set direction to output 19 | pin_set_dir(pin, 1); 20 | 21 | // Set to off 22 | pin_set(pin, 0); 23 | 24 | int pwm = atoi(argv[2]); 25 | 26 | if (pwm < 0 || pwm > 100) { 27 | printf("pwm '%d' from input '%s' not in %d..%d\n", pwm, argv[2], 0, 100); 28 | return 1; 29 | } 30 | 31 | printf("PWM on pin %s at %d%%. Press CTRL-C to exit.\n", pin->name, pwm); 32 | 33 | struct Uart * uart = stdio_uart; 34 | 35 | int step = 0; 36 | while(1) { 37 | if (step <= 0) { 38 | step = 100; 39 | } 40 | step--; 41 | if (step < pwm) { 42 | pin_set(pin, 1); 43 | } else { 44 | pin_set(pin, 0); 45 | } 46 | 47 | if (uart_can_read(uart)) { 48 | char c = uart_read(uart); 49 | if (c == 3) { 50 | break; 51 | } 52 | } 53 | } 54 | 55 | pin_set(pin, 0); 56 | } else { 57 | printf("no pwm provided in %d..%d\n", 0, 100); 58 | return 1; 59 | } 60 | } else { 61 | printf("no pin provided in %d..%d\n", 0, pin_count() - 1); 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /src/cmd/sleep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../cpu.h" 5 | #include 6 | 7 | int cmd_sleep(int argc, char ** argv) { 8 | if (argc > 1) { 9 | int num = atoi(argv[1]); 10 | if(num < 0) { 11 | printf("invalid argument '%s'\n", argv[1]); 12 | return 1; 13 | } else { 14 | int i; 15 | for(i = 0; i < num; i++) { 16 | _delay_ms(1000); 17 | } 18 | } 19 | } else { 20 | printf("no number\n"); 21 | return 1; 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/cmd/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../time.h" 3 | 4 | int cmd_time(int argc, char ** argv) { 5 | uint64_t time = time_get(); 6 | 7 | uint32_t seconds = (uint32_t)(time / 1000000ULL); 8 | uint32_t micros = (uint32_t)(time % 1000000ULL); 9 | 10 | printf("%ld.%06ld\n", seconds, micros); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "config.h" 3 | 4 | uint8_t eeprom_read_byte_or(uint8_t * address, uint8_t fallback) { 5 | uint8_t value = eeprom_read_byte(address); 6 | if (value == 0xFF) { 7 | return fallback; 8 | } else { 9 | return value; 10 | } 11 | } 12 | 13 | uint16_t eeprom_read_word_or(uint16_t * address, uint16_t fallback) { 14 | uint16_t value = eeprom_read_word(address); 15 | if (value == 0xFFFF) { 16 | return fallback; 17 | } else { 18 | return value; 19 | } 20 | } 21 | 22 | uint32_t eeprom_read_dword_or(uint32_t * address, uint32_t fallback) { 23 | uint32_t value = eeprom_read_dword(address); 24 | if (value == 0xFFFFFFFF) { 25 | return fallback; 26 | } else { 27 | return value; 28 | } 29 | } 30 | 31 | struct Config config_new() { 32 | #undef CFG_16 33 | #define CFG_16(N, V) .N = V ## U, 34 | 35 | #undef CFG_32 36 | #define CFG_32(N, V) .N = V ## UL, 37 | 38 | struct Config config = { 39 | CFGS 40 | }; 41 | 42 | return config; 43 | } 44 | 45 | void config_load(struct Config * config) { 46 | uint16_t addr = 0; 47 | 48 | #undef CFG_16 49 | #define CFG_16(N, V) \ 50 | config->N = eeprom_read_word_or((uint16_t *)addr, V); \ 51 | addr += 2; 52 | 53 | #undef CFG_32 54 | #define CFG_32(N, V) \ 55 | config->N = eeprom_read_dword_or((uint32_t *)addr, V); \ 56 | addr += 4; 57 | 58 | CFGS 59 | } 60 | 61 | void config_save(struct Config * config) { 62 | uint16_t addr = 0; 63 | 64 | #undef CFG_16 65 | #define CFG_16(N, V) \ 66 | eeprom_update_word((uint16_t *)addr, config->N); \ 67 | addr += 2; 68 | 69 | #undef CFG_32 70 | #define CFG_32(N, V) \ 71 | eeprom_update_dword((uint32_t *)addr, config->N); \ 72 | addr += 4; 73 | 74 | CFGS 75 | } 76 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define CFG_16(N, V) 5 | #define CFG_32(N, V) 6 | 7 | #define CFGS \ 8 | /* Fan frequency in Hz */ \ 9 | CFG_32(fan_frequency, 25000) \ 10 | /* Fan transition speed in us */ \ 11 | CFG_32(fan_transition, 1000) \ 12 | /* Fan duty default in 0..10000 */ \ 13 | CFG_16(fan_duty, 5000) \ 14 | /* LED frequency in Hz */ \ 15 | CFG_32(led_frequency, 125000) \ 16 | /* LED transition speed in us */ \ 17 | CFG_32(led_transition, 200000) \ 18 | /* LED suspend transition speed in us */ \ 19 | CFG_32(suspend_transition, 6000000) \ 20 | /* Off setting of power LED in 0..10000 */ \ 21 | /* 79, 157, and 235 are valid low settings */ \ 22 | CFG_16(powerbtn_off, 235) \ 23 | /* Max suspend setting of power LED in 0..10000 */ \ 24 | CFG_16(powerbtn_suspend, 2500) \ 25 | /* On setting of power LED in 0..10000 */ \ 26 | CFG_16(powerbtn_on, 3500) \ 27 | /* Pressed setting of power LED in 0..10000 */ \ 28 | CFG_16(powerbtn_pressed, 10000) \ 29 | /* CPU Fan timeout to return to motherboard control in us */ \ 30 | CFG_32(cpufan_timeout, 10000000) 31 | 32 | #undef CFG_16 33 | #define CFG_16(N, V) uint16_t N; 34 | 35 | #undef CFG_32 36 | #define CFG_32(N, V) uint32_t N; 37 | 38 | struct Config { 39 | CFGS 40 | }; 41 | 42 | struct Config config_new(); 43 | 44 | void config_load(struct Config * config); 45 | void config_save(struct Config * config); 46 | 47 | #endif // CONFIG_H 48 | -------------------------------------------------------------------------------- /src/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef CPU_H 2 | #define CPU_H 3 | 4 | #define F_CPU 16000000ULL 5 | 6 | #endif // CPU_H 7 | -------------------------------------------------------------------------------- /src/debounce.c: -------------------------------------------------------------------------------- 1 | #include "debounce.h" 2 | 3 | struct Debounce debounce_new(uint64_t timeout) { 4 | struct Debounce db = { 5 | .timeout = timeout, 6 | .time = 0, 7 | .value = 0 8 | }; 9 | 10 | return db; 11 | } 12 | 13 | uint8_t debounce_step(struct Debounce * debounce, uint64_t time, uint8_t value) { 14 | // If the signal has been debounced for the timeout, stop debouncing 15 | if (time >= debounce->time) { 16 | debounce->time = 0; 17 | } 18 | // If the signal is being debounced, keep doing so, and override value 19 | else if (debounce->time) { 20 | value = debounce->value; 21 | } 22 | // If the signal changed, start debouncing 23 | else if (value != debounce->value) { 24 | debounce->time = time + debounce->timeout; 25 | } 26 | 27 | debounce->value = value; 28 | 29 | return debounce->value; 30 | } 31 | -------------------------------------------------------------------------------- /src/debounce.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBOUNCE_H 2 | #define DEBOUNCE_H 3 | 4 | #include 5 | 6 | struct Debounce { 7 | // Total time to debounce 8 | uint64_t timeout; 9 | // When debouncing will end 10 | uint64_t time; 11 | // Last value of the signal 12 | uint8_t value; 13 | }; 14 | 15 | // Create debounce object with a timeout in microseconds 16 | struct Debounce debounce_new(uint64_t timeout); 17 | 18 | // Debounce, returning the current state of the signal with bounces removed 19 | uint8_t debounce_step(struct Debounce * debounce, uint64_t time, uint8_t value); 20 | 21 | #endif // DEBOUNCE_H 22 | -------------------------------------------------------------------------------- /src/device.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "device.h" 3 | 4 | struct Device device_new(char * name, struct Pin * input, struct Pin * output, struct Timer * output_timer, enum TimerChannel output_channel, uint16_t output_duty, uint64_t transition_time) { 5 | struct Device device = { 6 | .name = name, 7 | .command_time = 0, 8 | .input = input, 9 | .input_db = debounce_new(1000ULL), 10 | .input_last = 0, 11 | .input_state = INPUT_STATE_UNKNOWN, 12 | .input_tach = tach_new(1000000ULL), 13 | .output = output, 14 | .output_timer = output_timer, 15 | .output_channel = output_channel, 16 | .output_duty = output_duty, 17 | .output_trans = transition_new(10000.0 / (double)transition_time), 18 | .callback = NULL, 19 | .callback_data = NULL, 20 | }; 21 | 22 | return device; 23 | } 24 | 25 | void device_init(struct Device * device) { 26 | pin_set_dir(device->input, 0); 27 | pin_set(device->input, 1); 28 | 29 | pin_set_dir(device->output, 1); 30 | pin_set(device->output, 0); 31 | 32 | device->input_last = pin_get(device->input); 33 | device->input_db.value = device->input_last; 34 | 35 | timer_set_channel_mode(device->output_timer, device->output_channel, TIMER_CHANNEL_MODE_CLEAR); 36 | timer_set_channel_duty(device->output_timer, device->output_channel, device->output_duty); 37 | 38 | device->output_trans.value = (double)device->output_duty; 39 | } 40 | 41 | void device_set_callback(struct Device * device, DeviceCallback callback, void * callback_data) { 42 | device->callback = callback; 43 | device->callback_data = callback_data; 44 | } 45 | 46 | // Update device 47 | void device_step(struct Device * device, uint64_t time) { 48 | // Run input items 49 | uint8_t input = debounce_step(&device->input_db, time, pin_get(device->input)); 50 | if (input) { 51 | if (device->input_last) { 52 | device->input_state = INPUT_STATE_HIGH; 53 | } else { 54 | device->input_state = INPUT_STATE_RISING; 55 | } 56 | } else { 57 | if (device->input_last) { 58 | device->input_state = INPUT_STATE_FALLING; 59 | } else { 60 | device->input_state = INPUT_STATE_LOW; 61 | } 62 | } 63 | device->input_last = input; 64 | 65 | uint16_t tach_value = 0; 66 | switch (device->input_state) { 67 | case INPUT_STATE_RISING: 68 | tach_value = 1; 69 | break; 70 | default: 71 | break; 72 | } 73 | tach_step(&device->input_tach, time, tach_value); 74 | 75 | // Run callback 76 | if (device->callback) { 77 | device->callback(device, time, device->callback_data); 78 | } 79 | 80 | // Run output items 81 | uint16_t duty = (uint16_t)transition_step(&device->output_trans, time); 82 | if (duty != device->output_duty) { 83 | //TODO: Handle failure to set 84 | if (timer_set_channel_duty(device->output_timer, device->output_channel, duty) == 0) { 85 | device->output_duty = duty; 86 | } 87 | } 88 | } 89 | 90 | void device_destroy(struct Device * device) { 91 | timer_set_channel_duty(device->output_timer, device->output_channel, 0); 92 | timer_set_channel_mode(device->output_timer, device->output_channel, TIMER_CHANNEL_MODE_NORMAL); 93 | 94 | pin_set_dir(device->output, 0); 95 | pin_set(device->output, 0); 96 | 97 | pin_set_dir(device->input, 0); 98 | pin_set(device->input, 0); 99 | } 100 | -------------------------------------------------------------------------------- /src/device.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVICE_H 2 | #define DEVICE_H 3 | 4 | #include 5 | #include "debounce.h" 6 | #include "pin.h" 7 | #include "tach.h" 8 | #include "timer.h" 9 | #include "transition.h" 10 | 11 | struct Device; 12 | 13 | typedef void (*DeviceCallback)(struct Device * device, uint64_t time, void * data); 14 | 15 | enum InputState { 16 | INPUT_STATE_UNKNOWN = 0, 17 | INPUT_STATE_LOW = 1, 18 | INPUT_STATE_FALLING = 2, 19 | INPUT_STATE_RISING = 3, 20 | INPUT_STATE_HIGH = 4, 21 | }; 22 | 23 | struct Device { 24 | // Name of device 25 | char * name; 26 | // Time of last command 27 | uint64_t command_time; 28 | // Input pin 29 | struct Pin * input; 30 | // Debounce input pin to prevent erroneous input 31 | struct Debounce input_db; 32 | // Last value of input pin to detect rising and falling 33 | uint8_t input_last; 34 | // State of input pin: low, falling, rising, or high 35 | enum InputState input_state; 36 | // Count of input pin rises 37 | struct Tach input_tach; 38 | // Output pin 39 | struct Pin * output; 40 | // Timer used to drive output pin 41 | struct Timer * output_timer; 42 | // Channel of timer used to drive output pin 43 | enum TimerChannel output_channel; 44 | // Duty cycle of output pin, from 0 to 10000 45 | uint16_t output_duty; 46 | // Smoothly transition output pin between duty cycles 47 | struct Transition output_trans; 48 | // Run a callback every time the device is updated 49 | DeviceCallback callback; 50 | // Provide custom data to the callback 51 | void * callback_data; 52 | }; 53 | 54 | struct Device device_new(char * name, struct Pin * input, struct Pin * output, struct Timer * output_timer, enum TimerChannel output_channel, uint16_t output_duty, uint64_t transition_time); 55 | 56 | void device_init(struct Device * device); 57 | 58 | void device_set_callback(struct Device * device, DeviceCallback callback, void * callback_data); 59 | 60 | void device_step(struct Device * device, uint64_t time); 61 | 62 | void device_destroy(struct Device * device); 63 | 64 | #endif // DEVICE_H 65 | -------------------------------------------------------------------------------- /src/i2c/master.c: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/g4lvanix/I2C-master-lib 2 | 3 | #include 4 | #include 5 | #include 6 | #include "../cpu.h" 7 | #include "mod.h" 8 | #include "master.h" 9 | 10 | #define TIMEOUT (F_CPU/1000) 11 | 12 | #define I2C_READ 0x01 13 | #define I2C_WRITE 0x00 14 | 15 | uint8_t i2c_start(uint8_t address, uint8_t rw) { 16 | uint32_t count; 17 | 18 | // reset TWI control register 19 | TWCR = 0; 20 | // transmit START condition 21 | TWCR = (1< 0) count -= 1; 25 | if (count == 0) { 26 | printf("i2c_start timed out waiting to send start to 0x%X\n", address); 27 | return 1; 28 | } 29 | 30 | // check if the start condition was successfully transmitted 31 | if((TWSR & 0xF8) != TW_START){ 32 | printf("i2c_start failed to send start condition to 0x%X\n", address); 33 | return 1; 34 | } 35 | 36 | // load slave address into data register 37 | TWDR = ((address << 1) | rw); 38 | // start transmission of address 39 | TWCR = (1< 0) count -= 1; 43 | if (count == 0) { 44 | printf("i2c_start timed out waiting to send address to 0x%X\n", address); 45 | return 1; 46 | } 47 | 48 | // check if the device has acknowledged the READ / WRITE mode 49 | uint8_t twst = TW_STATUS & 0xF8; 50 | if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) { 51 | printf("i2c_start failed to receive ack from 0x%X\n", address); 52 | return 1; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | void i2c_stop() { 59 | // transmit STOP condition 60 | TWCR = (1< 0) count -= 1; 73 | if (count == 0) { 74 | printf("i2c_write timed out waiting to send 0x%X\n", data); 75 | return 1; 76 | } 77 | 78 | if( (TWSR & 0xF8) != TW_MT_DATA_ACK ){ 79 | printf("i2c_write failed to receive ack for 0x%X\n", data); 80 | return 1; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | uint8_t i2c_read_ack() { 87 | // start TWI module and acknowledge data after reception 88 | TWCR = (1< 2 | #include "../cpu.h" 3 | #include "mod.h" 4 | 5 | void i2c_init() { 6 | TWAR = 0; 7 | TWBR = (uint8_t)(((F_CPU / F_SCL) - 16 ) / 2); 8 | TWCR = 0; 9 | } 10 | -------------------------------------------------------------------------------- /src/i2c/mod.h: -------------------------------------------------------------------------------- 1 | // SCL frequency 2 | #define F_SCL 50000UL 3 | 4 | void i2c_init(); 5 | -------------------------------------------------------------------------------- /src/i2c/slave.c: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/thegouger/avr-i2c-slave 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../cpu.h" 8 | #include "mod.h" 9 | #include "slave.h" 10 | 11 | void (* volatile i2c_slave_new_cb)() = NULL; 12 | void (* volatile i2c_slave_recv_cb)(uint8_t) = NULL; 13 | uint8_t (* volatile i2c_slave_send_cb)() = NULL; 14 | 15 | void i2c_slave_init(uint8_t address, void (*new_cb)(), void (*recv_cb)(uint8_t), uint8_t (*send_cb)()){ 16 | // ensure correct behavior by stopping before changing callbacks or address 17 | i2c_slave_stop(); 18 | 19 | // clear interrupts 20 | cli(); 21 | 22 | // setup callbacks 23 | i2c_slave_new_cb = new_cb; 24 | i2c_slave_recv_cb = recv_cb; 25 | i2c_slave_send_cb = send_cb; 26 | // load address into TWI address register 27 | TWAR = (address << 1); 28 | // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt 29 | TWCR = (1< 100 | #define KEY_SLASH 0x38 // Keyboard / and ? 101 | #define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock 102 | 103 | #define KEY_F1 0x3a // Keyboard F1 104 | #define KEY_F2 0x3b // Keyboard F2 105 | #define KEY_F3 0x3c // Keyboard F3 106 | #define KEY_F4 0x3d // Keyboard F4 107 | #define KEY_F5 0x3e // Keyboard F5 108 | #define KEY_F6 0x3f // Keyboard F6 109 | #define KEY_F7 0x40 // Keyboard F7 110 | #define KEY_F8 0x41 // Keyboard F8 111 | #define KEY_F9 0x42 // Keyboard F9 112 | #define KEY_F10 0x43 // Keyboard F10 113 | #define KEY_F11 0x44 // Keyboard F11 114 | #define KEY_F12 0x45 // Keyboard F12 115 | 116 | #define KEY_SYSRQ 0x46 // Keyboard Print Screen 117 | #define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock 118 | #define KEY_PAUSE 0x48 // Keyboard Pause 119 | #define KEY_INSERT 0x49 // Keyboard Insert 120 | #define KEY_HOME 0x4a // Keyboard Home 121 | #define KEY_PAGEUP 0x4b // Keyboard Page Up 122 | #define KEY_DELETE 0x4c // Keyboard Delete Forward 123 | #define KEY_END 0x4d // Keyboard End 124 | #define KEY_PAGEDOWN 0x4e // Keyboard Page Down 125 | #define KEY_RIGHT 0x4f // Keyboard Right Arrow 126 | #define KEY_LEFT 0x50 // Keyboard Left Arrow 127 | #define KEY_DOWN 0x51 // Keyboard Down Arrow 128 | #define KEY_UP 0x52 // Keyboard Up Arrow 129 | 130 | #define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear 131 | #define KEY_KPSLASH 0x54 // Keypad / 132 | #define KEY_KPASTERISK 0x55 // Keypad * 133 | #define KEY_KPMINUS 0x56 // Keypad - 134 | #define KEY_KPPLUS 0x57 // Keypad + 135 | #define KEY_KPENTER 0x58 // Keypad ENTER 136 | #define KEY_KP1 0x59 // Keypad 1 and End 137 | #define KEY_KP2 0x5a // Keypad 2 and Down Arrow 138 | #define KEY_KP3 0x5b // Keypad 3 and PageDn 139 | #define KEY_KP4 0x5c // Keypad 4 and Left Arrow 140 | #define KEY_KP5 0x5d // Keypad 5 141 | #define KEY_KP6 0x5e // Keypad 6 and Right Arrow 142 | #define KEY_KP7 0x5f // Keypad 7 and Home 143 | #define KEY_KP8 0x60 // Keypad 8 and Up Arrow 144 | #define KEY_KP9 0x61 // Keypad 9 and Page Up 145 | #define KEY_KP0 0x62 // Keypad 0 and Insert 146 | #define KEY_KPDOT 0x63 // Keypad . and Delete 147 | 148 | #define KEY_102ND 0x64 // Keyboard Non-US \ and | 149 | #define KEY_COMPOSE 0x65 // Keyboard Application 150 | #define KEY_POWER 0x66 // Keyboard Power 151 | #define KEY_KPEQUAL 0x67 // Keypad = 152 | 153 | #define KEY_F13 0x68 // Keyboard F13 154 | #define KEY_F14 0x69 // Keyboard F14 155 | #define KEY_F15 0x6a // Keyboard F15 156 | #define KEY_F16 0x6b // Keyboard F16 157 | #define KEY_F17 0x6c // Keyboard F17 158 | #define KEY_F18 0x6d // Keyboard F18 159 | #define KEY_F19 0x6e // Keyboard F19 160 | #define KEY_F20 0x6f // Keyboard F20 161 | #define KEY_F21 0x70 // Keyboard F21 162 | #define KEY_F22 0x71 // Keyboard F22 163 | #define KEY_F23 0x72 // Keyboard F23 164 | #define KEY_F24 0x73 // Keyboard F24 165 | 166 | #define KEY_OPEN 0x74 // Keyboard Execute 167 | #define KEY_HELP 0x75 // Keyboard Help 168 | #define KEY_PROPS 0x76 // Keyboard Menu 169 | #define KEY_FRONT 0x77 // Keyboard Select 170 | #define KEY_STOP 0x78 // Keyboard Stop 171 | #define KEY_AGAIN 0x79 // Keyboard Again 172 | #define KEY_UNDO 0x7a // Keyboard Undo 173 | #define KEY_CUT 0x7b // Keyboard Cut 174 | #define KEY_COPY 0x7c // Keyboard Copy 175 | #define KEY_PASTE 0x7d // Keyboard Paste 176 | #define KEY_FIND 0x7e // Keyboard Find 177 | #define KEY_MUTE 0x7f // Keyboard Mute 178 | #define KEY_VOLUMEUP 0x80 // Keyboard Volume Up 179 | #define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down 180 | // 0x82 Keyboard Locking Caps Lock 181 | // 0x83 Keyboard Locking Num Lock 182 | // 0x84 Keyboard Locking Scroll Lock 183 | #define KEY_KPCOMMA 0x85 // Keypad Comma 184 | // 0x86 Keypad Equal Sign 185 | #define KEY_RO 0x87 // Keyboard International1 186 | #define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2 187 | #define KEY_YEN 0x89 // Keyboard International3 188 | #define KEY_HENKAN 0x8a // Keyboard International4 189 | #define KEY_MUHENKAN 0x8b // Keyboard International5 190 | #define KEY_KPJPCOMMA 0x8c // Keyboard International6 191 | // 0x8d Keyboard International7 192 | // 0x8e Keyboard International8 193 | // 0x8f Keyboard International9 194 | #define KEY_HANGEUL 0x90 // Keyboard LANG1 195 | #define KEY_HANJA 0x91 // Keyboard LANG2 196 | #define KEY_KATAKANA 0x92 // Keyboard LANG3 197 | #define KEY_HIRAGANA 0x93 // Keyboard LANG4 198 | #define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5 199 | // 0x95 Keyboard LANG6 200 | // 0x96 Keyboard LANG7 201 | // 0x97 Keyboard LANG8 202 | // 0x98 Keyboard LANG9 203 | // 0x99 Keyboard Alternate Erase 204 | // 0x9a Keyboard SysReq/Attention 205 | // 0x9b Keyboard Cancel 206 | // 0x9c Keyboard Clear 207 | // 0x9d Keyboard Prior 208 | // 0x9e Keyboard Return 209 | // 0x9f Keyboard Separator 210 | // 0xa0 Keyboard Out 211 | // 0xa1 Keyboard Oper 212 | // 0xa2 Keyboard Clear/Again 213 | // 0xa3 Keyboard CrSel/Props 214 | // 0xa4 Keyboard ExSel 215 | 216 | // 0xb0 Keypad 00 217 | // 0xb1 Keypad 000 218 | // 0xb2 Thousands Separator 219 | // 0xb3 Decimal Separator 220 | // 0xb4 Currency Unit 221 | // 0xb5 Currency Sub-unit 222 | #define KEY_KPLEFTPAREN 0xb6 // Keypad ( 223 | #define KEY_KPRIGHTPAREN 0xb7 // Keypad ) 224 | // 0xb8 Keypad { 225 | // 0xb9 Keypad } 226 | // 0xba Keypad Tab 227 | // 0xbb Keypad Backspace 228 | // 0xbc Keypad A 229 | // 0xbd Keypad B 230 | // 0xbe Keypad C 231 | // 0xbf Keypad D 232 | // 0xc0 Keypad E 233 | // 0xc1 Keypad F 234 | // 0xc2 Keypad XOR 235 | // 0xc3 Keypad ^ 236 | // 0xc4 Keypad % 237 | // 0xc5 Keypad < 238 | // 0xc6 Keypad > 239 | // 0xc7 Keypad & 240 | // 0xc8 Keypad && 241 | // 0xc9 Keypad | 242 | // 0xca Keypad || 243 | // 0xcb Keypad : 244 | // 0xcc Keypad # 245 | // 0xcd Keypad Space 246 | // 0xce Keypad @ 247 | // 0xcf Keypad ! 248 | // 0xd0 Keypad Memory Store 249 | // 0xd1 Keypad Memory Recall 250 | // 0xd2 Keypad Memory Clear 251 | // 0xd3 Keypad Memory Add 252 | // 0xd4 Keypad Memory Subtract 253 | // 0xd5 Keypad Memory Multiply 254 | // 0xd6 Keypad Memory Divide 255 | // 0xd7 Keypad +/- 256 | // 0xd8 Keypad Clear 257 | // 0xd9 Keypad Clear Entry 258 | // 0xda Keypad Binary 259 | // 0xdb Keypad Octal 260 | // 0xdc Keypad Decimal 261 | // 0xdd Keypad Hexadecimal 262 | 263 | #define KEY_LEFTCTRL 0xe0 // Keyboard Left Control 264 | #define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift 265 | #define KEY_LEFTALT 0xe2 // Keyboard Left Alt 266 | #define KEY_LEFTMETA 0xe3 // Keyboard Left GUI 267 | #define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control 268 | #define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift 269 | #define KEY_RIGHTALT 0xe6 // Keyboard Right Alt 270 | #define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI 271 | 272 | #define KEY_MEDIA_PLAYPAUSE 0xe8 273 | #define KEY_MEDIA_STOPCD 0xe9 274 | #define KEY_MEDIA_PREVIOUSSONG 0xea 275 | #define KEY_MEDIA_NEXTSONG 0xeb 276 | #define KEY_MEDIA_EJECTCD 0xec 277 | #define KEY_MEDIA_VOLUMEUP 0xed 278 | #define KEY_MEDIA_VOLUMEDOWN 0xee 279 | #define KEY_MEDIA_MUTE 0xef 280 | #define KEY_MEDIA_WWW 0xf0 281 | #define KEY_MEDIA_BACK 0xf1 282 | #define KEY_MEDIA_FORWARD 0xf2 283 | #define KEY_MEDIA_STOP 0xf3 284 | #define KEY_MEDIA_FIND 0xf4 285 | #define KEY_MEDIA_SCROLLUP 0xf5 286 | #define KEY_MEDIA_SCROLLDOWN 0xf6 287 | #define KEY_MEDIA_EDIT 0xf7 288 | #define KEY_MEDIA_SLEEP 0xf8 289 | #define KEY_MEDIA_COFFEE 0xf9 290 | // Using for special function #define KEY_MEDIA_REFRESH 0xfa 291 | // Using for special function #define KEY_MEDIA_CALC 0xfb 292 | 293 | #endif // USB_HID_KEYS 294 | -------------------------------------------------------------------------------- /src/keyboard/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../macro.h" 3 | #include "mod.h" 4 | #include "hid.h" 5 | 6 | #define K(NAME, I, J) { str(NAME), KEY_ ## NAME, KEY_ ## NAME, I, J } 7 | #define KF(NAME, FN_NAME, I, J) { str(NAME), KEY_ ## NAME, KEY_ ## FN_NAME, I, J } 8 | 9 | #define MAP 2 10 | 11 | #if MAP == 1 12 | struct Key key_map[] = { 13 | // Row 1 14 | K(ESC, 14, 15), 15 | KF(F1, NONE, 15, 17), 16 | KF(F2, NONE, 12, 17), 17 | KF(F3, MUTE, 15, 16), 18 | K(F4, 12, 16), 19 | KF(F5, VOLUMEDOWN, 15, 19), 20 | KF(F6, VOLUMEUP, 12, 19), 21 | KF(F7, NONE, 15, 18), 22 | KF(F8, BRIGHTNESSDOWN, 12, 18), 23 | KF(F9, BRIGHTNESSUP, 15, 21), 24 | KF(F10, NONE, 12, 21), 25 | KF(F11, AIRPLANE, 15, 20), 26 | KF(F12, NONE, 10, 20), 27 | K(SYSRQ, 4, 20), 28 | K(PAUSE, 12, 22), 29 | KF(INSERT, SCROLLLOCK, 10, 23), 30 | KF(DELETE, NUMLOCK, 15, 23), 31 | K(KPSLASH, 2, 19), 32 | K(KPASTERISK, 2, 18), 33 | 34 | // Row 2 35 | KF(GRAVE, MEDIA_PLAYPAUSE, 12, 14), 36 | K(1, 10, 14), 37 | K(2, 11, 14), 38 | K(3, 10, 17), 39 | K(4, 11, 17), 40 | K(5, 6, 17), 41 | K(6, 10, 16), 42 | K(7, 11, 16), 43 | K(8, 10, 19), 44 | K(9, 11, 19), 45 | K(0, 10, 18), 46 | K(MINUS, 10, 21), 47 | K(EQUAL, 11, 20), 48 | K(BACKSPACE, 6, 23), 49 | K(KPDOT, 2, 16), 50 | K(KPPLUS, 13, 15), 51 | K(KPMINUS, 9, 15), 52 | 53 | // Row 3 54 | K(TAB, 9, 11), 55 | K(Q, 9, 10), 56 | K(W, 2, 13), 57 | K(E, 11, 13), 58 | K(R, 8, 10), 59 | K(T, 10, 13), 60 | K(Y, 6, 8), 61 | K(U, 4, 17), 62 | K(I, 6, 16), 63 | K(O, 6, 19), 64 | K(P, 11, 18), 65 | K(LEFTBRACE, 11, 21), 66 | K(RIGHTBRACE, 6, 20), 67 | K(BACKSLASH, 12, 23), 68 | K(KP7, 4, 23), 69 | K(KP8, 5, 18), 70 | K(KP9, 10, 22), 71 | 72 | // Row 4 73 | K(CAPSLOCK, 4, 9), 74 | K(A, 6, 9), 75 | K(S, 4, 8), 76 | K(D, 6, 14), 77 | K(F, 4, 13), 78 | K(G, 6, 13), 79 | K(H, 5, 17), 80 | K(J, 5, 16), 81 | K(K, 4, 14), 82 | K(L, 4, 19), 83 | K(SEMICOLON, 6, 18), 84 | K(APOSTROPHE, 6, 21), 85 | K(ENTER, 4, 22), 86 | K(KP4, 12, 13), 87 | K(KP5, 11, 23), 88 | K(KP6, 15, 22), 89 | 90 | // Row 5 91 | K(LEFTSHIFT, 7, 15), 92 | K(Z, 5, 9), 93 | K(X, 2, 9), 94 | K(C, 5, 13), 95 | K(V, 2, 8), 96 | K(B, 5, 8), 97 | K(N, 5, 14), 98 | K(M, 4, 16), 99 | K(COMMA, 5, 19), 100 | K(DOT, 4, 18), 101 | K(SLASH, 4, 21), 102 | K(RIGHTSHIFT, 7, 12), 103 | KF(UP, PAGEUP, 5, 23), 104 | K(KP1, 1, 4), 105 | K(KP2, 1, 6), 106 | K(KP3, 1, 11), 107 | 108 | // Row 6 109 | K(LEFTCTRL, 0, 12), 110 | K(FN, 1, 5), 111 | K(LEFTMETA, 1, 10), 112 | K(LEFTALT, 3, 12), 113 | K(SPACE, 9, 12), 114 | K(RIGHTALT, 3, 15), 115 | K(COMPOSE, 2, 22), 116 | K(RIGHTCTRL, 0, 15), 117 | KF(LEFT, HOME, 1, 2), 118 | KF(DOWN, PAGEDOWN, 11, 22), 119 | KF(RIGHT, END, 12, 20), 120 | K(KP0, 2, 17), 121 | K(KPENTER, 2, 14), 122 | 123 | K(POWER, 24, 25), 124 | 125 | { NULL, 0, 0, 0 } 126 | }; 127 | #elif MAP == 2 128 | struct Key key_map[] = { 129 | // Row 1 130 | K(ESC, 14, 15), 131 | KF(F1, NONE, 15, 17), 132 | KF(F2, NONE, 12, 17), 133 | KF(F3, MUTE, 15, 16), 134 | K(F4, 12, 16), 135 | KF(F5, VOLUMEDOWN, 15, 19), 136 | KF(F6, VOLUMEUP, 12, 19), 137 | KF(F7, NONE, 15, 18), 138 | KF(F8, BRIGHTNESSDOWN, 12, 18), 139 | KF(F9, BRIGHTNESSUP, 15, 21), 140 | KF(F10, NONE, 12, 21), 141 | KF(F11, AIRPLANE, 15, 20), 142 | KF(F12, NONE, 10, 20), 143 | K(SYSRQ, 4, 20), 144 | KF(INSERT, SCROLLLOCK, 10, 23), 145 | K(DELETE, 15, 23), 146 | K(HOME, 1, 12), 147 | K(END, 1, 15), 148 | KF(PAGEUP, PAUSE, 12, 23), 149 | KF(PAGEDOWN, NONE, 12, 22), 150 | 151 | // Row 2 152 | KF(GRAVE, MEDIA_PLAYPAUSE, 12, 14), 153 | K(1, 10, 14), 154 | K(2, 11, 14), 155 | K(3, 10, 17), 156 | K(4, 11, 17), 157 | K(5, 11, 18), 158 | K(6, 10, 16), 159 | K(7, 11, 16), 160 | K(8, 10, 19), 161 | K(9, 11, 19), 162 | K(0, 10, 18), 163 | K(MINUS, 10, 21), 164 | K(EQUAL, 11, 20), 165 | K(BACKSPACE, 6, 22), 166 | K(NUMLOCK, 8, 15), 167 | K(KPSLASH, 2, 19), 168 | K(KPASTERISK, 2, 18), 169 | K(KPMINUS, 9, 15), 170 | 171 | // Row 3 172 | K(TAB, 5, 9), 173 | K(Q, 4, 9), 174 | K(W, 2, 13), 175 | K(E, 11, 13), 176 | K(R, 6, 9), 177 | K(T, 5, 13), 178 | K(Y, 6, 8), 179 | K(U, 4, 17), 180 | K(I, 6, 16), 181 | K(O, 6, 19), 182 | K(P, 6, 17), 183 | K(LEFTBRACE, 11, 21), 184 | K(RIGHTBRACE, 6, 23), 185 | K(BACKSLASH, 2, 20), 186 | K(KP7, 4, 23), 187 | K(KP8, 5, 18), 188 | K(KP9, 10, 22), 189 | K(KPPLUS, 13, 15), 190 | 191 | // Row 4 192 | K(CAPSLOCK, 8, 11), 193 | K(A, 6, 20), 194 | K(S, 4, 8), 195 | K(D, 9, 10), 196 | K(F, 9, 11), 197 | K(G, 2, 9), 198 | K(H, 5, 17), 199 | K(J, 5, 16), 200 | K(K, 4, 14), 201 | K(L, 4, 19), 202 | K(SEMICOLON, 6, 18), 203 | K(APOSTROPHE, 6, 21), 204 | K(ENTER, 4, 22), 205 | K(KP4, 12, 13), 206 | K(KP5, 11, 23), 207 | K(KP6, 15, 22), 208 | 209 | // Row 5 210 | K(LEFTSHIFT, 7, 15), 211 | K(Z, 8, 10), 212 | K(X, 6, 14), 213 | K(C, 4, 13), 214 | K(V, 2, 8), 215 | K(B, 5, 8), 216 | K(N, 5, 14), 217 | K(M, 4, 16), 218 | K(COMMA, 5, 19), 219 | K(DOT, 4, 18), 220 | K(SLASH, 4, 21), 221 | K(RIGHTSHIFT, 7, 12), 222 | K(UP, 5, 23), 223 | K(KP1, 1, 4), 224 | K(KP2, 1, 6), 225 | K(KP3, 1, 11), 226 | K(KPENTER, 2, 14), 227 | 228 | // Row 6 229 | K(LEFTCTRL, 0, 12), 230 | K(FN, 1, 5), 231 | K(LEFTMETA, 1, 10), 232 | K(LEFTALT, 3, 12), 233 | K(SPACE, 9, 12), 234 | K(RIGHTALT, 3, 15), 235 | K(COMPOSE, 2, 22), 236 | K(RIGHTCTRL, 0, 15), 237 | K(LEFT, 1, 2), 238 | K(DOWN, 11, 22), 239 | K(RIGHT, 12, 20), 240 | K(KP0, 2, 17), 241 | 242 | K(POWER, 24, 25), 243 | 244 | { NULL, 0, 0, 0 } 245 | }; 246 | #endif 247 | -------------------------------------------------------------------------------- /src/keyboard/mod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mod.h" 3 | 4 | struct Key * key_new(int i, int j) { 5 | int name_i = 0; 6 | for (name_i = 0; ; name_i++) { 7 | struct Key * k = &key_map[name_i]; 8 | if (k->name == NULL) { 9 | return NULL; 10 | } 11 | if (k->i == i && k->j == j) { 12 | return k; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/keyboard/mod.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define KEY_PINS 26 4 | 5 | struct Key { 6 | char * name; 7 | uint16_t hid; 8 | uint16_t fn_hid; 9 | int i; 10 | int j; 11 | }; 12 | 13 | extern const char * key_pin_names[KEY_PINS]; 14 | extern int key_pins[KEY_PINS]; 15 | 16 | extern struct Key key_map[]; 17 | 18 | struct Key * key_new(int i, int j); 19 | -------------------------------------------------------------------------------- /src/keyboard/pins.c: -------------------------------------------------------------------------------- 1 | #include "mod.h" 2 | 3 | #define FLIP_H 0 4 | #define FLIP_V 0 5 | 6 | #if FLIP_V && FLIP_H 7 | const char * key_pin_names[KEY_PINS] = { 8 | // 42 - 49 - L block 9 | //"PL0", "PL1", 10 | //"PL2", "PL3", 11 | "PL4", "PL5", 12 | "PL6", "PL7", 13 | 14 | // 38 - 41 - Not a block :(yyiufgnhdjydjydjydjydj 15 | "PG0", "PG1", 16 | "PG2", "PD7", 17 | 18 | // 30 - 27 - C block 19 | "PC0", "PC1", 20 | "PC2", "PC3", 21 | "PC4", "PC5", 22 | "PC6", "PC7", 23 | 24 | // 22 - 29 - A block 25 | "PA7", "PA6", 26 | "PA5", "PA4", 27 | "PA3", "PA2", 28 | "PA1", "PA0", 29 | 30 | "PL2", "PL3", 31 | }; 32 | 33 | int key_pins[KEY_PINS] = { 34 | // 42 - 49 - L block 35 | //0x50, 0x51, 36 | //0x52, 0x53, 37 | 0x54, 0x55, 38 | 0x56, 0x57, 39 | 40 | // 38 - 41 - Not a block :( 41 | 0x30, 0x31, 42 | 0x32, 0x1F, 43 | 44 | // 30 - 27 - C block 45 | 0x10, 0x11, 46 | 0x12, 0x13, 47 | 0x14, 0x15, 48 | 0x16, 0x17, 49 | 50 | // 22 - 29, A block 51 | 0x07, 0x06, 52 | 0x05, 0x04, 53 | 0x03, 0x02, 54 | 0x01, 0x00, 55 | 56 | 0x52, 0x53, 57 | }; 58 | #elif FLIP_V 59 | const char * key_pin_names[KEY_PINS] = { 60 | // 22 - 29 - A block 61 | "PA1", "PA0", 62 | "PA3", "PA2", 63 | "PA5", "PA4", 64 | "PA7", "PA6", 65 | 66 | // 30 - 27 - C block 67 | "PC6", "PC7", 68 | "PC4", "PC5", 69 | "PC2", "PC3", 70 | "PC0", "PC1", 71 | 72 | // 38 - 41 - Not a block :( 73 | "PG2", "PD7", 74 | "PG0", "PG1", 75 | 76 | // 42 - 49 - L block 77 | "PL6", "PL7", 78 | "PL4", "PL5", 79 | //"PL3", "PL2", 80 | //"PL1", "PL0" 81 | 82 | "PL3", "PL2", 83 | }; 84 | 85 | int key_pins[KEY_PINS] = { 86 | // 22 - 29, A block 87 | 0x01, 0x00, 88 | 0x03, 0x02, 89 | 0x05, 0x04, 90 | 0x07, 0x06, 91 | 92 | // 30 - 27 - C block 93 | 0x16, 0x17, 94 | 0x14, 0x15, 95 | 0x12, 0x13, 96 | 0x10, 0x11, 97 | 98 | // 38 - 41 - Not a block :( 99 | 0x32, 0x1F, 100 | 0x30, 0x31, 101 | 102 | // 42 - 49 - L block 103 | 0x56, 0x57, 104 | 0x54, 0x55, 105 | //0x53, 0x52, 106 | //0x51, 0x50 107 | 108 | 0x53, 0x52, 109 | }; 110 | #elif FLIP_H 111 | const char * key_pin_names[KEY_PINS] = { 112 | // 42 - 49 - L block 113 | //"PL1", "PL0" 114 | //"PL3", "PL2", 115 | "PL5", "PL4", 116 | "PL7", "PL6", 117 | 118 | // 38 - 41 - Not a block :( 119 | "PG1", "PG0", 120 | "PD7", "PG2", 121 | 122 | // 30 - 27 - C block 123 | "PC1", "PC0", 124 | "PC3", "PC2", 125 | "PC5", "PC4", 126 | "PC7", "PC6", 127 | 128 | // 22 - 29 - A block 129 | "PA6", "PA7", 130 | "PA4", "PA5", 131 | "PA2", "PA3", 132 | "PA0", "PA1", 133 | 134 | "PL3", "PL2", 135 | }; 136 | 137 | int key_pins[KEY_PINS] = { 138 | // 42 - 49 - L block 139 | //0x51, 0x50 140 | //0x53, 0x52, 141 | 0x55, 0x54, 142 | 0x57, 0x56, 143 | 144 | // 38 - 41 - Not a block :( 145 | 0x31, 0x30, 146 | 0x1F, 0x32, 147 | 148 | // 30 - 27 - C block 149 | 0x11, 0x10, 150 | 0x13, 0x12, 151 | 0x15, 0x14, 152 | 0x17, 0x16, 153 | 154 | // 22 - 29, A block 155 | 0x06, 0x07, 156 | 0x04, 0x05, 157 | 0x02, 0x03, 158 | 0x00, 0x01, 159 | 160 | 0x53, 0x52, 161 | }; 162 | #else 163 | const char * key_pin_names[KEY_PINS] = { 164 | // 22 - 29 - A block 165 | "PA0", "PA1", 166 | "PA2", "PA3", 167 | "PA4", "PA5", 168 | "PA6", "PA7", 169 | 170 | // 30 - 27 - C block 171 | "PC7", "PC6", 172 | "PC5", "PC4", 173 | "PC3", "PC2", 174 | "PC1", "PC0", 175 | 176 | // 38 - 41 - Not a block :( 177 | "PD7", "PG2", 178 | "PG1", "PG0", 179 | 180 | // 42 - 49 - L block 181 | "PL7", "PL6", 182 | "PL5", "PL4", 183 | //"PL3", "PL2", 184 | //"PL1", "PL0" 185 | 186 | "PL3", "PL2", 187 | }; 188 | 189 | int key_pins[KEY_PINS] = { 190 | // 22 - 29, A block 191 | 0x00, 0x01, 192 | 0x02, 0x03, 193 | 0x04, 0x05, 194 | 0x06, 0x07, 195 | 196 | // 30 - 27 - C block 197 | 0x17, 0x16, 198 | 0x15, 0x14, 199 | 0x13, 0x12, 200 | 0x11, 0x10, 201 | 202 | // 38 - 41 - Not a block :( 203 | 0x1F, 0x32, 204 | 0x31, 0x30, 205 | 206 | // 42 - 49 - L block 207 | 0x57, 0x56, 208 | 0x55, 0x54, 209 | //0x53, 0x52, 210 | //0x51, 0x50 211 | 212 | 0x53, 0x52, 213 | }; 214 | #endif 215 | -------------------------------------------------------------------------------- /src/macro.h: -------------------------------------------------------------------------------- 1 | #ifndef MACRO_H 2 | #define MACRO_H 3 | 4 | #define xstr(s) str(s) 5 | #define str(s) #s 6 | 7 | #endif // MACRO_H 8 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cmd/mod.h" 5 | //#include "i2c/mod.h" 6 | #include "macro.h" 7 | #include "readline.h" 8 | #include "stdio.h" 9 | #include "time.h" 10 | #include "uart.h" 11 | 12 | int cmd_thelio(int argc, char ** argv); 13 | 14 | int main(){ 15 | stdio_init(0, 1000000); 16 | time_init(); 17 | //i2c_init(); 18 | sei(); 19 | 20 | cmd_thelio(0, NULL); 21 | 22 | // for (;;) { 23 | // printf("io@%s> ", xstr(__DEVICE__)); 24 | // 25 | // char line[64] = { 0 }; 26 | // int line_len = readline(line, 63); 27 | // line[line_len] = 0; 28 | // 29 | // run(line); 30 | // } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/pin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "macro.h" 5 | #include "pin.h" 6 | 7 | #define PORT_BLOCK(BLOCK) \ 8 | {"P" xstr(BLOCK) "0", 0, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 9 | {"P" xstr(BLOCK) "1", 1, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 10 | {"P" xstr(BLOCK) "2", 2, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 11 | {"P" xstr(BLOCK) "3", 3, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 12 | {"P" xstr(BLOCK) "4", 4, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 13 | {"P" xstr(BLOCK) "5", 5, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 14 | {"P" xstr(BLOCK) "6", 6, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK}, \ 15 | {"P" xstr(BLOCK) "7", 7, &DDR ## BLOCK, &PIN ## BLOCK, &PORT ## BLOCK} 16 | 17 | #if defined(__AVR_ATmega32U4__) 18 | static struct Pin PINS[] = { 19 | // 0x00 20 | PORT_BLOCK(B), 21 | // 0x08 22 | PORT_BLOCK(C), 23 | // 0x10 24 | PORT_BLOCK(D), 25 | // 0x18 26 | PORT_BLOCK(E), 27 | // 0x20 28 | PORT_BLOCK(F), 29 | }; 30 | #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 31 | static struct Pin PINS[] = { 32 | // 0x00 33 | PORT_BLOCK(A), 34 | // 0x08 35 | PORT_BLOCK(B), 36 | // 0x10 37 | PORT_BLOCK(C), 38 | // 0x18 39 | PORT_BLOCK(D), 40 | // 0x20 41 | PORT_BLOCK(E), 42 | // 0x28 43 | PORT_BLOCK(F), 44 | // 0x30 45 | PORT_BLOCK(G), 46 | // 0x38 47 | PORT_BLOCK(H), 48 | // 0x40 49 | PORT_BLOCK(J), 50 | // 0x48 51 | PORT_BLOCK(K), 52 | // 0x50 53 | PORT_BLOCK(L), 54 | }; 55 | #else 56 | #error "Could not find pin definitions" 57 | #endif 58 | 59 | int pin_count() { 60 | return sizeof(PINS)/sizeof(struct Pin); 61 | } 62 | 63 | struct Pin * pin_new(int num) { 64 | if (num < pin_count()) { 65 | return &PINS[num]; 66 | } else { 67 | return NULL; 68 | } 69 | } 70 | 71 | struct Pin * pin_from_name(char * name) { 72 | int num; 73 | for (num = 0; num < pin_count(); num++) { 74 | struct Pin * pin = &PINS[num]; 75 | if (strcmp(pin->name, name) == 0) { 76 | return pin; 77 | } 78 | } 79 | return NULL; 80 | } 81 | 82 | unsigned char pin_get_dir(struct Pin * pin) { 83 | if (*pin->ddr & (1 << pin->shift)) { 84 | return 1; 85 | } else { 86 | return 0; 87 | } 88 | } 89 | 90 | void pin_set_dir(struct Pin * pin, unsigned char value) { 91 | if (value) { 92 | *pin->ddr |= (1 << pin->shift); 93 | } else { 94 | *pin->ddr &= ~(1 << pin->shift); 95 | } 96 | } 97 | 98 | unsigned char pin_get(struct Pin * pin) { 99 | if (*pin->pin & (1 << pin->shift)) { 100 | return 1; 101 | } else { 102 | return 0; 103 | } 104 | } 105 | 106 | void pin_set(struct Pin * pin, unsigned char value) { 107 | if (value) { 108 | *pin->port |= (1 << pin->shift); 109 | } else { 110 | *pin->port &= ~(1 << pin->shift); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/pin.h: -------------------------------------------------------------------------------- 1 | #ifndef PIN_H 2 | #define PIN_H 3 | 4 | #include 5 | 6 | struct Pin { 7 | char * name; 8 | int shift; 9 | volatile uint8_t * ddr; 10 | volatile uint8_t * pin; 11 | volatile uint8_t * port; 12 | }; 13 | 14 | int pin_count(); 15 | struct Pin * pin_new(int num); 16 | struct Pin * pin_from_name(char * name); 17 | 18 | unsigned char pin_get_dir(struct Pin * pin); 19 | void pin_set_dir(struct Pin * pin, unsigned char value); 20 | 21 | unsigned char pin_get(struct Pin * pin); 22 | void pin_set(struct Pin * pin, unsigned char value); 23 | 24 | #endif // PIN_H 25 | -------------------------------------------------------------------------------- /src/readline.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int readline(char * buf, int len) { 4 | int escape = 0; 5 | int i = 0; 6 | while (i < len) { 7 | char c = getchar(); 8 | if (escape == 2) { 9 | // Throw away CSI codes 10 | if (c >= 0x40 || c <= 0x7E) { 11 | escape = 0; 12 | } 13 | } else if (escape == 1) { 14 | // Throw away escape codes, detect CSI codes 15 | if (c == '[') { 16 | escape = 2; 17 | } else { 18 | escape = 0; 19 | } 20 | } else if (c == 0x1B) { 21 | // Detect escape codes 22 | escape = 1; 23 | } else if (c == 0x7F) { 24 | // Handle backspace 25 | if(i > 0) { 26 | i -= 1; 27 | putchar('\b'); 28 | putchar(' '); 29 | putchar('\b'); 30 | } 31 | } else if(c == 3) { 32 | // Break on CTRL-C 33 | putchar('\r'); 34 | putchar('\n'); 35 | return 0; 36 | } else if(c == '\r' || c == '\n') { 37 | // Return on enter 38 | putchar('\r'); 39 | putchar('\n'); 40 | break; 41 | } else if(c == '\t') { 42 | // Ignore tab 43 | } else { 44 | putchar(c); 45 | buf[i] = c; 46 | i += 1; 47 | } 48 | } 49 | return i; 50 | } 51 | -------------------------------------------------------------------------------- /src/readline.h: -------------------------------------------------------------------------------- 1 | #ifndef READLINE_H 2 | #define READLINE_H 3 | 4 | int readline(char * buf, int len); 5 | 6 | #endif // READLINE_H 7 | -------------------------------------------------------------------------------- /src/stdio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "uart.h" 3 | 4 | struct Uart * stdio_uart = NULL; 5 | 6 | int stdio_get(FILE * stream) { 7 | return (int)uart_read(stdio_uart); 8 | } 9 | 10 | int stdio_put(char data, FILE * stream) { 11 | if (data == '\n') { 12 | uart_write(stdio_uart, '\r'); 13 | } 14 | uart_write(stdio_uart, (unsigned char)data); 15 | return 0; 16 | } 17 | 18 | FILE stdio_file = FDEV_SETUP_STREAM(stdio_put, stdio_get, _FDEV_SETUP_RW); 19 | 20 | void stdio_init(int num, unsigned long baud) { 21 | struct Uart * uart = uart_new(num); 22 | if(uart != NULL) { 23 | uart_init(uart, baud); 24 | stdio_uart = uart; 25 | stdin = stdout = stderr = &stdio_file; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef STDIO_H 2 | #define STDIO_H 3 | 4 | extern struct Uart * stdio_uart; 5 | void stdio_init(int num, unsigned long baud); 6 | 7 | #endif // STDIO_H 8 | -------------------------------------------------------------------------------- /src/tach.c: -------------------------------------------------------------------------------- 1 | #include "tach.h" 2 | 3 | struct Tach tach_new(uint64_t timeout) { 4 | struct Tach tach = { 5 | .timeout = timeout, 6 | .time = 0, 7 | .count = 0, 8 | .value = 0, 9 | }; 10 | 11 | return tach; 12 | } 13 | 14 | uint16_t tach_step(struct Tach * tach, uint64_t time, uint16_t value) { 15 | tach->count += value; 16 | if (time >= tach->time) { 17 | tach->time = time + tach->timeout; 18 | tach->value = tach->count; 19 | tach->count = 0; 20 | } 21 | return tach->value; 22 | } 23 | -------------------------------------------------------------------------------- /src/tach.h: -------------------------------------------------------------------------------- 1 | #ifndef TACH_H 2 | #define TACH_H 3 | 4 | #include 5 | 6 | struct Tach { 7 | uint64_t timeout; 8 | uint64_t time; 9 | uint16_t count; 10 | uint16_t value; 11 | }; 12 | 13 | struct Tach tach_new(uint64_t timeout); 14 | 15 | uint16_t tach_step(struct Tach * tach, uint64_t time, uint16_t value); 16 | 17 | #endif // TACH_H 18 | -------------------------------------------------------------------------------- /src/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cpu.h" 5 | 6 | // We will use CS02 to divide clock by 256 7 | #define TIME_TCNT_SCALE 256ULL 8 | // Dividing 16 MHz by 256 = 62.5 KHz 9 | #define TIME_TCNT_FREQUENCY (F_CPU/TIME_TCNT_SCALE) 10 | // 62.5 KHz is 16 us resolution 11 | #define TIME_TCNT_INTERVAL (1000000ULL/TIME_TCNT_FREQUENCY) 12 | // 250 is an optimal factor, it is the square root of 625,000 13 | #define TIME_OVERFLOW_SCALE 250ULL 14 | // Dividing 62.5 KHz by 250 = 250 Hz 15 | #define TIME_OVERFLOW_FREQUENCY (TIME_TCNT_FREQUENCY/TIME_OVERFLOW_SCALE) 16 | // 250 Hz is 4 ms resolution 17 | #define TIME_OVERFLOW_INTERVAL (1000000ULL/TIME_OVERFLOW_FREQUENCY) 18 | 19 | // At 4 milliseconds per tick, an overflow of this variable would happen after 2,338,218,130 years 20 | static volatile uint64_t time_overflows = 0; 21 | 22 | static volatile uint8_t * time_tcnt = &TCNT0; 23 | 24 | ISR(TIMER0_COMPA_vect) { 25 | time_overflows++; 26 | } 27 | 28 | void time_init(void) { 29 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 30 | // Disable interrupts 31 | TIMSK0 = 0; 32 | // Clear COM set WGM mode[0:1] 33 | TCCR0A = _BV(WGM01); 34 | // Clear WGM mode[2] and set CS to 256 35 | TCCR0B = _BV(CS02); 36 | // Reset timer to 0 37 | TCNT0 = 0; 38 | // Set overflow value to that calculated above 39 | OCR0A = TIME_OVERFLOW_SCALE; 40 | // Clear current overflows 41 | time_overflows = 0; 42 | // Enable interrupts 43 | TIMSK0 = _BV(OCF0A); 44 | } 45 | } 46 | 47 | uint64_t time_get(void) { 48 | uint64_t overflows; 49 | uint8_t tcnt_0; 50 | uint8_t tcnt_1; 51 | 52 | do { 53 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 54 | tcnt_0 = *(time_tcnt); 55 | } 56 | // If an interrupt happens here, tcnt will have changed and so will overflows 57 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 58 | overflows = time_overflows; 59 | tcnt_1 = *(time_tcnt); 60 | } 61 | } while (tcnt_0 != tcnt_1); 62 | 63 | return overflows * TIME_OVERFLOW_INTERVAL + ((uint64_t)tcnt_0) * TIME_TCNT_INTERVAL; 64 | } 65 | 66 | void time_set(uint64_t time) { 67 | uint64_t overflows = time / TIME_OVERFLOW_INTERVAL; 68 | uint8_t tcnt = (uint8_t)((time % TIME_OVERFLOW_INTERVAL) / TIME_TCNT_INTERVAL); 69 | 70 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 71 | time_overflows = overflows; 72 | *(time_tcnt) = tcnt; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/time.h: -------------------------------------------------------------------------------- 1 | #ifndef TIME_H 2 | #define TIME_H 3 | 4 | #include 5 | 6 | void time_init(void); 7 | uint64_t time_get(void); 8 | void time_set(uint64_t time); 9 | 10 | #endif // TIME_H 11 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpu.h" 4 | #include "macro.h" 5 | #include "timer.h" 6 | 7 | #define TIMER_CHANNEL(N, P) \ 8 | { &OCR ## N ## P, _BV(COM ## N ## P ## 0), _BV(COM ## N ## P ## 1), } 9 | 10 | #define TIMER_8(N) \ 11 | { \ 12 | xstr(N), \ 13 | TIMER_8, \ 14 | .timer_8 = { \ 15 | &TCCR ## N ## A, \ 16 | &TCCR ## N ## B, \ 17 | &TCNT ## N, \ 18 | &TIMSK ## N, \ 19 | &TIFR ## N, \ 20 | { \ 21 | TIMER_CHANNEL(N, A), \ 22 | TIMER_CHANNEL(N, B), \ 23 | }, \ 24 | } \ 25 | } 26 | 27 | #define TIMER_16(N) \ 28 | { \ 29 | xstr(N), \ 30 | TIMER_16, \ 31 | .timer_16 = { \ 32 | &TCCR ## N ## A, \ 33 | &TCCR ## N ## B, \ 34 | &TCCR ## N ## C, \ 35 | &TCNT ## N, \ 36 | &ICR ## N, \ 37 | &TIMSK ## N, \ 38 | &TIFR ## N, \ 39 | { \ 40 | _BV(CS ## N ## 0), \ 41 | _BV(CS ## N ## 1), \ 42 | _BV(CS ## N ## 2), \ 43 | }, \ 44 | { \ 45 | _BV(WGM ## N ## 0), \ 46 | _BV(WGM ## N ## 1), \ 47 | _BV(WGM ## N ## 2), \ 48 | _BV(WGM ## N ## 3), \ 49 | }, \ 50 | { \ 51 | TIMER_CHANNEL(N, A), \ 52 | TIMER_CHANNEL(N, B), \ 53 | TIMER_CHANNEL(N, C), \ 54 | }, \ 55 | } \ 56 | } 57 | 58 | #define TIMER_10(N) \ 59 | { \ 60 | xstr(N), \ 61 | TIMER_10, \ 62 | .timer_10 = { \ 63 | &TCCR ## N ## A, \ 64 | &TCCR ## N ## B, \ 65 | &TCCR ## N ## C, \ 66 | &TCCR ## N ## D, \ 67 | &TCCR ## N ## E, \ 68 | &TCNT ## N, \ 69 | &TIMSK ## N, \ 70 | &TIFR ## N, \ 71 | &DT ## N, \ 72 | { \ 73 | _BV(CS ## N ## 0), \ 74 | _BV(CS ## N ## 1), \ 75 | _BV(CS ## N ## 2), \ 76 | _BV(CS ## N ## 3), \ 77 | }, \ 78 | { \ 79 | _BV(WGM ## N ## 0), \ 80 | _BV(WGM ## N ## 1), \ 81 | }, \ 82 | { \ 83 | _BV(PWM ## N ## A), \ 84 | _BV(PWM ## N ## B), \ 85 | _BV(PWM ## N ## D), \ 86 | }, \ 87 | { \ 88 | TIMER_CHANNEL(N, A), \ 89 | TIMER_CHANNEL(N, B), \ 90 | { &OCR ## N ## C, 0, 0, }, \ 91 | TIMER_CHANNEL(N, D), \ 92 | }, \ 93 | } \ 94 | } 95 | 96 | #if defined(__AVR_ATmega32U4__) 97 | static struct Timer TIMERS[] = { 98 | TIMER_8(0), //Timer 0 is used by system time, it is not recommended to use it 99 | TIMER_16(1), 100 | TIMER_16(3), 101 | TIMER_10(4) 102 | }; 103 | #else 104 | #error "Could not find timer definitions" 105 | #endif 106 | 107 | int timer_count() { 108 | return sizeof(TIMERS)/sizeof(struct Timer); 109 | } 110 | 111 | struct Timer * timer_new(int num) { 112 | if (num < timer_count()) { 113 | return &TIMERS[num]; 114 | } else { 115 | return NULL; 116 | } 117 | } 118 | 119 | struct Timer * timer_from_name(char * name) { 120 | int num; 121 | for (num = 0; num < timer_count(); num++) { 122 | struct Timer * timer = &TIMERS[num]; 123 | if (strcmp(timer->name, name) == 0) { 124 | return timer; 125 | } 126 | } 127 | return NULL; 128 | } 129 | 130 | uint8_t timer_16_init(struct Timer16 * timer) { 131 | *(timer->tccra) = 0; 132 | *(timer->tccrb) = timer->cs[0]; 133 | *(timer->tccrc) = 0; 134 | *(timer->tcnt) = 0; 135 | for (int i = 0; i < sizeof(timer->chans)/sizeof(struct Timer16Channel); i++) { 136 | *(timer->chans[i].ocr) = 0; 137 | } 138 | *(timer->icr) = 0; 139 | *(timer->timsk) = 0; 140 | *(timer->tifr) = *(timer->tifr); 141 | 142 | return 0; 143 | } 144 | 145 | uint8_t timer_10_init(struct Timer10 * timer) { 146 | *(timer->tccra) = 0; 147 | *(timer->tccrb) = timer->cs[0]; 148 | *(timer->tccrc) = 0; 149 | *(timer->tccrd) = 0; 150 | *(timer->tccre) = 0; 151 | *(timer->tcnt) = 0; 152 | for (int i = 0; i < sizeof(timer->chans)/sizeof(struct Timer10Channel); i++) { 153 | *(timer->chans[i].ocr) = 0; 154 | } 155 | *(timer->timsk) = 0; 156 | *(timer->tifr) = *(timer->tifr); 157 | *(timer->dt) = 0; 158 | 159 | return 0; 160 | } 161 | 162 | uint8_t timer_init(struct Timer * timer) { 163 | switch (timer->kind) { 164 | case TIMER_16: 165 | return timer_16_init(&timer->timer_16); 166 | case TIMER_10: 167 | return timer_10_init(&timer->timer_10); 168 | default: 169 | return 1; 170 | } 171 | } 172 | 173 | uint8_t timer_16_set_frequency(struct Timer16 * timer, uint32_t frequency) { 174 | uint32_t top = F_CPU/frequency; 175 | if (top > 0xFFFF) { 176 | return 1; 177 | } 178 | 179 | *(timer->icr) = (uint16_t)top; 180 | 181 | return 0; 182 | } 183 | 184 | uint8_t timer_10_set_frequency(struct Timer10 * timer, uint32_t frequency) { 185 | uint32_t top = F_CPU/frequency; 186 | if (top > 0xFF) { 187 | return 1; 188 | } 189 | 190 | *(timer->chans[TIMER_CHANNEL_C].ocr) = (uint8_t)top; 191 | 192 | return 0; 193 | } 194 | 195 | uint8_t timer_set_frequency(struct Timer * timer, uint32_t frequency) { 196 | switch (timer->kind) { 197 | case TIMER_16: 198 | return timer_16_set_frequency(&timer->timer_16, frequency); 199 | case TIMER_10: 200 | return timer_10_set_frequency(&timer->timer_10, frequency); 201 | default: 202 | return 1; 203 | } 204 | } 205 | 206 | uint8_t timer_16_set_mode(struct Timer16 * timer, enum TimerMode mode) { 207 | uint8_t tccra = *(timer->tccra); 208 | uint8_t tccrb = *(timer->tccrb); 209 | 210 | tccra &= ~(timer->wgm[0] | timer->wgm[1]); 211 | tccrb &= ~(timer->wgm[2] | timer->wgm[3]); 212 | 213 | switch (mode) { 214 | case TIMER_MODE_NORMAL: 215 | break; 216 | case TIMER_MODE_CTC: 217 | tccrb |= timer->wgm[3] | timer->wgm[2]; 218 | break; 219 | case TIMER_MODE_FAST: 220 | tccra |= timer->wgm[1]; 221 | tccrb |= timer->wgm[3] | timer->wgm[2]; 222 | break; 223 | case TIMER_MODE_PHASE: 224 | tccra |= timer->wgm[1]; 225 | tccrb |= timer->wgm[3]; 226 | break; 227 | case TIMER_MODE_PHASE_FREQ: 228 | tccrb |= timer->wgm[3]; 229 | break; 230 | default: 231 | return 1; 232 | } 233 | 234 | *(timer->tccra) = tccra; 235 | *(timer->tccrb) = tccrb; 236 | 237 | return 0; 238 | } 239 | 240 | uint8_t timer_10_set_mode(struct Timer10 * timer, enum TimerMode mode) { 241 | uint8_t tccra = *(timer->tccra); 242 | uint8_t tccrc = *(timer->tccrc); 243 | uint8_t tccrd = *(timer->tccrd); 244 | 245 | tccra &= ~(timer->pwm[0] | timer->pwm[1]); 246 | tccrc &= ~(timer->pwm[2]); 247 | tccrd &= ~(timer->wgm[0] | timer->wgm[1]); 248 | 249 | switch (mode) { 250 | case TIMER_MODE_PHASE_FREQ: 251 | tccrd |= timer->wgm[0]; 252 | //Fallthrough to set PWM pins 253 | case TIMER_MODE_FAST: 254 | tccra |= timer->pwm[0] | timer->pwm[1]; 255 | tccrc |= timer->pwm[2]; 256 | break; 257 | case TIMER_MODE_NORMAL: 258 | break; 259 | default: 260 | return 1; 261 | } 262 | 263 | *(timer->tccra) = tccra; 264 | *(timer->tccrc) = tccrc; 265 | *(timer->tccrd) = tccrd; 266 | 267 | return 0; 268 | } 269 | 270 | uint8_t timer_set_mode(struct Timer * timer, enum TimerMode mode) { 271 | switch (timer->kind) { 272 | case TIMER_16: 273 | return timer_16_set_mode(&timer->timer_16, mode); 274 | case TIMER_10: 275 | return timer_10_set_mode(&timer->timer_10, mode); 276 | default: 277 | return 1; 278 | } 279 | } 280 | 281 | uint8_t timer_channels(struct Timer * timer) { 282 | switch (timer->kind) { 283 | case TIMER_8: 284 | return sizeof(timer->timer_8.chans)/sizeof(struct Timer8Channel); 285 | case TIMER_16: 286 | return sizeof(timer->timer_16.chans)/sizeof(struct Timer16Channel); 287 | case TIMER_10: 288 | return sizeof(timer->timer_10.chans)/sizeof(struct Timer10Channel); 289 | default: 290 | return 0; 291 | } 292 | } 293 | 294 | uint8_t timer_16_set_channel_duty(struct Timer16 * timer, enum TimerChannel channel, uint16_t duty) { 295 | if (channel >= sizeof(timer->chans)/sizeof(struct Timer16Channel)) { 296 | return 1; 297 | } 298 | 299 | struct Timer16Channel * chan = &timer->chans[channel]; 300 | 301 | uint32_t top = (uint32_t)(*(timer->icr)); 302 | uint32_t bottom = ((top * ((uint32_t)duty)) / TIMER_DUTY_MAX); 303 | if (bottom > top) { 304 | bottom = top; 305 | } 306 | 307 | *(chan->ocr) = (uint16_t)bottom; 308 | 309 | return 0; 310 | } 311 | 312 | uint8_t timer_10_set_channel_duty(struct Timer10 * timer, enum TimerChannel channel, uint16_t duty) { 313 | if (channel >= sizeof(timer->chans)/sizeof(struct Timer10Channel)) { 314 | return 1; 315 | } 316 | 317 | struct Timer10Channel * chan = &timer->chans[channel]; 318 | 319 | uint32_t top = (uint32_t)(*(timer->chans[TIMER_CHANNEL_C].ocr)); 320 | uint32_t bottom = ((top * ((uint32_t)duty)) / TIMER_DUTY_MAX); 321 | if (bottom > top) { 322 | bottom = top; 323 | } 324 | 325 | *(chan->ocr) = (uint8_t)bottom; 326 | 327 | return 0; 328 | } 329 | 330 | uint8_t timer_set_channel_duty(struct Timer * timer, enum TimerChannel channel, uint16_t duty) { 331 | switch (timer->kind) { 332 | case TIMER_16: 333 | return timer_16_set_channel_duty(&timer->timer_16, channel, duty); 334 | case TIMER_10: 335 | return timer_10_set_channel_duty(&timer->timer_10, channel, duty); 336 | default: 337 | return 1; 338 | } 339 | } 340 | 341 | uint8_t timer_16_set_channel_mode(struct Timer16 * timer, enum TimerChannel channel, enum TimerChannelMode mode) { 342 | if (channel >= sizeof(timer->chans)/sizeof(struct Timer16Channel)) { 343 | return 1; 344 | } 345 | 346 | struct Timer16Channel * chan = &timer->chans[channel]; 347 | 348 | uint8_t tccra = *(timer->tccra); 349 | 350 | if (mode & 0b01) { 351 | tccra |= chan->com0; 352 | } else { 353 | tccra &= ~(chan->com0); 354 | } 355 | 356 | if (mode & 0b10) { 357 | tccra |= chan->com1; 358 | } else { 359 | tccra &= ~(chan->com1); 360 | } 361 | 362 | *(timer->tccra) = tccra; 363 | 364 | return 0; 365 | } 366 | 367 | uint8_t timer_10_set_channel_mode(struct Timer10 * timer, enum TimerChannel channel, enum TimerChannelMode mode) { 368 | if (channel >= sizeof(timer->chans)/sizeof(struct Timer10Channel)) { 369 | return 1; 370 | } 371 | 372 | struct Timer10Channel * chan = &timer->chans[channel]; 373 | 374 | uint8_t tccra = *(timer->tccra); 375 | 376 | if (mode & 0b01) { 377 | tccra |= chan->com0; 378 | } else { 379 | tccra &= ~(chan->com0); 380 | } 381 | 382 | if (mode & 0b10) { 383 | tccra |= chan->com1; 384 | } else { 385 | tccra &= ~(chan->com1); 386 | } 387 | 388 | *(timer->tccra) = tccra; 389 | 390 | return 0; 391 | } 392 | 393 | uint8_t timer_set_channel_mode(struct Timer * timer, enum TimerChannel channel, enum TimerChannelMode mode) { 394 | if (channel >= timer_channels(timer)) { 395 | return 1; 396 | } 397 | 398 | switch (timer->kind) { 399 | case TIMER_16: 400 | return timer_16_set_channel_mode(&timer->timer_16, channel, mode); 401 | case TIMER_10: 402 | return timer_10_set_channel_mode(&timer->timer_10, channel, mode); 403 | default: 404 | return 1; 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | enum TimerKind { 5 | TIMER_8 = 8, 6 | TIMER_16 = 16, 7 | TIMER_10 = 10, 8 | }; 9 | 10 | struct Timer8Channel { 11 | volatile uint8_t * ocr; 12 | uint8_t com0; 13 | uint8_t com1; 14 | }; 15 | 16 | struct Timer8 { 17 | volatile uint8_t * tccra; 18 | volatile uint8_t * tccrb; 19 | volatile uint8_t * tcnt; 20 | volatile uint8_t * timsk; 21 | volatile uint8_t * tifr; 22 | struct Timer8Channel chans[2]; 23 | }; 24 | 25 | struct Timer16Channel { 26 | volatile uint16_t * ocr; 27 | uint8_t com0; 28 | uint8_t com1; 29 | }; 30 | 31 | struct Timer16 { 32 | volatile uint8_t * tccra; 33 | volatile uint8_t * tccrb; 34 | volatile uint8_t * tccrc; 35 | volatile uint16_t * tcnt; 36 | volatile uint16_t * icr; 37 | volatile uint8_t * timsk; 38 | volatile uint8_t * tifr; 39 | uint8_t cs[3]; 40 | uint8_t wgm[4]; 41 | struct Timer16Channel chans[3]; 42 | }; 43 | 44 | struct Timer10Channel { 45 | volatile uint8_t * ocr; 46 | uint8_t com0; 47 | uint8_t com1; 48 | }; 49 | 50 | struct Timer10 { 51 | volatile uint8_t * tccra; 52 | volatile uint8_t * tccrb; 53 | volatile uint8_t * tccrc; 54 | volatile uint8_t * tccrd; 55 | volatile uint8_t * tccre; 56 | volatile uint16_t * tcnt; 57 | volatile uint8_t * timsk; 58 | volatile uint8_t * tifr; 59 | volatile uint8_t * dt; 60 | uint8_t cs[4]; 61 | uint8_t wgm[2]; 62 | uint8_t pwm[3]; 63 | struct Timer10Channel chans[4]; 64 | }; 65 | 66 | struct Timer { 67 | char * name; 68 | enum TimerKind kind; 69 | union { 70 | struct Timer8 timer_8; 71 | struct Timer16 timer_16; 72 | struct Timer10 timer_10; 73 | }; 74 | }; 75 | 76 | enum TimerMode { 77 | TIMER_MODE_NORMAL, 78 | TIMER_MODE_CTC, 79 | TIMER_MODE_FAST, 80 | TIMER_MODE_PHASE, 81 | TIMER_MODE_PHASE_FREQ, 82 | }; 83 | 84 | enum TimerChannel { 85 | TIMER_CHANNEL_A = 0, 86 | TIMER_CHANNEL_B = 1, 87 | TIMER_CHANNEL_C = 2, 88 | TIMER_CHANNEL_D = 3, 89 | }; 90 | 91 | enum TimerChannelMode { 92 | TIMER_CHANNEL_MODE_NORMAL = 0b00, 93 | TIMER_CHANNEL_MODE_TOGGLE = 0b01, 94 | TIMER_CHANNEL_MODE_CLEAR = 0b10, 95 | TIMER_CHANNEL_MODE_SET = 0b11, 96 | }; 97 | 98 | #define TIMER_DUTY_MAX 10000UL 99 | 100 | int timer_count(); 101 | struct Timer * timer_new(int num); 102 | struct Timer * timer_from_name(char * name); 103 | 104 | uint8_t timer_init(struct Timer * timer); 105 | uint8_t timer_set_frequency(struct Timer * timer, uint32_t frequency); 106 | uint8_t timer_set_mode(struct Timer * timer, enum TimerMode mode); 107 | 108 | uint8_t timer_channels(struct Timer * timer); 109 | uint8_t timer_set_channel_duty(struct Timer * timer, enum TimerChannel channel, uint16_t duty); 110 | uint8_t timer_set_channel_mode(struct Timer * timer, enum TimerChannel channel, enum TimerChannelMode mode); 111 | 112 | #endif // TIMER_H 113 | -------------------------------------------------------------------------------- /src/tokenize.c: -------------------------------------------------------------------------------- 1 | int tokenize(char delim, char * line, int line_len, char ** buf, int buf_len) { 2 | int argc = 0; 3 | 4 | int i = 0; 5 | char last = delim; 6 | while(i < line_len) { 7 | char c = line[i]; 8 | if(c == delim) { 9 | line[i] = 0; 10 | } else if(last == delim) { 11 | if (argc < buf_len) { 12 | buf[argc] = &line[i]; 13 | argc += 1; 14 | } 15 | } 16 | last = c; 17 | i += 1; 18 | } 19 | 20 | return argc; 21 | } -------------------------------------------------------------------------------- /src/tokenize.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENIZE_H 2 | #define TOKENIZE_H 3 | 4 | int tokenize(char delim, char * line, int line_len, char ** buf, int buf_len); 5 | 6 | #endif // TOKENIZE_H 7 | -------------------------------------------------------------------------------- /src/transition.c: -------------------------------------------------------------------------------- 1 | #include "transition.h" 2 | 3 | struct Transition transition_new(double speed) { 4 | struct Transition transition = { 5 | .speed = speed, 6 | .time = 0, 7 | .start = 0.0, 8 | .target = 0.0, 9 | .value = 0.0, 10 | }; 11 | 12 | return transition; 13 | } 14 | 15 | void transition_set(struct Transition * transition, uint64_t time, double target) { 16 | transition->time = time; 17 | transition->start = transition->value; 18 | transition->target = target; 19 | } 20 | 21 | double transition_step(struct Transition * transition, uint64_t time) { 22 | if (transition->time) { 23 | double interval = (double)(time - transition->time); 24 | double delta = interval * transition->speed; 25 | 26 | if (transition->start > transition->target) { 27 | transition->value = transition->start - delta; 28 | if (transition->value < transition->target) { 29 | transition->value = transition->target; 30 | transition->time = 0; 31 | } 32 | } else if (transition->start < transition->target) { 33 | transition->value = transition->start + delta; 34 | if (transition->value > transition->target) { 35 | transition->value = transition->target; 36 | transition->time = 0; 37 | } 38 | } else { 39 | transition->time = 0; 40 | } 41 | } 42 | 43 | return transition->value; 44 | } 45 | -------------------------------------------------------------------------------- /src/transition.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITION_H 2 | #define TRANSITION_H 3 | 4 | #include 5 | 6 | struct Transition { 7 | double speed; 8 | uint64_t time; 9 | double start; 10 | double target; 11 | double value; 12 | }; 13 | 14 | struct Transition transition_new(double speed); 15 | 16 | void transition_set(struct Transition * transition, uint64_t time, double target); 17 | double transition_step(struct Transition * transition, uint64_t time); 18 | 19 | #endif // TRANSITION_H 20 | -------------------------------------------------------------------------------- /src/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpu.h" 4 | #include "uart.h" 5 | 6 | #define UART(N) \ 7 | { \ 8 | &UCSR ## N ## A, \ 9 | &UCSR ## N ## B, \ 10 | &UCSR ## N ## C, \ 11 | &UDR ## N, \ 12 | &UBRR ## N ## L, \ 13 | &UBRR ## N ## H, \ 14 | _BV(RXC ## N), \ 15 | _BV(UDRE ## N), \ 16 | 0, \ 17 | _BV(RXEN ## N) | _BV(TXEN ## N), \ 18 | _BV(UCSZ ## N ## 1) | _BV(UCSZ ## N ## 0) \ 19 | } 20 | 21 | #if defined(__AVR_ATmega32U4__) 22 | static struct Uart UARTS[] = { 23 | UART(1) 24 | }; 25 | #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 26 | static struct Uart UARTS[] = { 27 | UART(0), 28 | UART(1), 29 | UART(2), 30 | UART(3) 31 | }; 32 | #else 33 | #error "Could not find UART definitions" 34 | #endif 35 | 36 | int uart_count() { 37 | return sizeof(UARTS)/sizeof(struct Uart); 38 | } 39 | 40 | struct Uart * uart_new(int num) { 41 | if (num < uart_count()) { 42 | return &UARTS[num]; 43 | } else { 44 | return NULL; 45 | } 46 | } 47 | 48 | void uart_init(struct Uart * uart, unsigned long baud) { 49 | unsigned long baud_prescale = (F_CPU / (baud * 16UL)) - 1; 50 | *(uart->baud_h) = (uint8_t)(baud_prescale>>8); 51 | *(uart->baud_l) = (uint8_t)(baud_prescale); 52 | *(uart->a) = uart->a_init; 53 | *(uart->b) = uart->b_init; 54 | *(uart->c) = uart->c_init; 55 | } 56 | 57 | unsigned char uart_can_read(struct Uart * uart) { 58 | return (*(uart->a)) & uart->a_read; 59 | } 60 | 61 | unsigned char uart_read(struct Uart * uart) { 62 | while (!uart_can_read(uart)) ; 63 | return *(uart->data); 64 | } 65 | 66 | unsigned char uart_can_write(struct Uart * uart) { 67 | return (*(uart->a)) & uart->a_write; 68 | } 69 | 70 | void uart_write(struct Uart * uart, unsigned char data) { 71 | while (!uart_can_write(uart)) ; 72 | *(uart->data) = data; 73 | } 74 | -------------------------------------------------------------------------------- /src/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART_H 2 | #define UART_H 3 | 4 | struct Uart { 5 | volatile uint8_t * a; 6 | volatile uint8_t * b; 7 | volatile uint8_t * c; 8 | volatile uint8_t * data; 9 | volatile uint8_t * baud_l; 10 | volatile uint8_t * baud_h; 11 | uint8_t a_read; 12 | uint8_t a_write; 13 | uint8_t a_init; 14 | uint8_t b_init; 15 | uint8_t c_init; 16 | }; 17 | 18 | void uart_init(struct Uart * uart, unsigned long baud); 19 | 20 | int uart_count(); 21 | struct Uart * uart_new(int num); 22 | 23 | unsigned char uart_can_read(struct Uart * uart); 24 | unsigned char uart_can_write(struct Uart * uart); 25 | 26 | unsigned char uart_read(struct Uart * uart); 27 | void uart_write(struct Uart * uart, unsigned char data); 28 | 29 | #endif // UART_H 30 | -------------------------------------------------------------------------------- /usb/.gitignore: -------------------------------------------------------------------------------- 1 | /obj/ 2 | *.a 3 | *.bin 4 | *.eep 5 | *.elf 6 | *.hex 7 | *.lss 8 | *.map 9 | *.sym 10 | -------------------------------------------------------------------------------- /usb/Config/LUFAConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * \brief LUFA Library Configuration Header File 33 | * 34 | * This header file is used to configure LUFA's compile time options, 35 | * as an alternative to the compile time constants supplied through 36 | * a makefile. 37 | * 38 | * For information on what each token does, refer to the LUFA 39 | * manual section "Summary of Compile Tokens". 40 | */ 41 | 42 | #ifndef _LUFA_CONFIG_H_ 43 | #define _LUFA_CONFIG_H_ 44 | 45 | #if (ARCH == ARCH_AVR8) 46 | 47 | /* Non-USB Related Configuration Tokens: */ 48 | // #define DISABLE_TERMINAL_CODES 49 | 50 | /* USB Class Driver Related Tokens: */ 51 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 52 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 53 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 54 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 55 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 56 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 57 | // #define NO_CLASS_DRIVER_AUTOFLUSH 58 | 59 | /* General USB Driver Related Tokens: */ 60 | // #define ORDERED_EP_CONFIG 61 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) 62 | #define USB_DEVICE_ONLY 63 | // #define USB_HOST_ONLY 64 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 65 | // #define NO_LIMITED_CONTROLLER_CONNECT 66 | // #define NO_SOF_EVENTS 67 | 68 | /* USB Device Mode Driver Related Tokens: */ 69 | // #define USE_RAM_DESCRIPTORS 70 | #define USE_FLASH_DESCRIPTORS 71 | // #define USE_EEPROM_DESCRIPTORS 72 | // #define NO_INTERNAL_SERIAL 73 | #define FIXED_CONTROL_ENDPOINT_SIZE 8 74 | #define DEVICE_STATE_AS_GPIOR 0 75 | #define FIXED_NUM_CONFIGURATIONS 1 76 | // #define CONTROL_ONLY_DEVICE 77 | #define INTERRUPT_CONTROL_ENDPOINT 78 | // #define NO_DEVICE_REMOTE_WAKEUP 79 | // #define NO_DEVICE_SELF_POWER 80 | 81 | /* USB Host Mode Driver Related Tokens: */ 82 | // #define HOST_STATE_AS_GPIOR 0 83 | // #define USB_HOST_TIMEOUT_MS {Insert Value Here} 84 | // #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} 85 | // #define NO_AUTO_VBUS_MANAGEMENT 86 | // #define INVERTED_VBUS_ENABLE_LINE 87 | 88 | #else 89 | 90 | #error Unsupported architecture for this LUFA configuration file. 91 | 92 | #endif 93 | #endif 94 | -------------------------------------------------------------------------------- /usb/Descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * USB Device Descriptors, for library use when in USB device mode. Descriptors are special 34 | * computer-readable structures which the host requests upon device enumeration, to determine 35 | * the device's capabilities and functions. 36 | */ 37 | 38 | #include "Descriptors.h" 39 | 40 | 41 | /** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall 42 | * device characteristics, including the supported USB version, control endpoint size and the 43 | * number of device configurations. The descriptor is read out by the USB host when the enumeration 44 | * process begins. 45 | */ 46 | const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = 47 | { 48 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 49 | 50 | .USBSpecification = VERSION_BCD(1,1,0), 51 | .Class = CDC_CSCP_CDCClass, 52 | .SubClass = CDC_CSCP_NoSpecificSubclass, 53 | .Protocol = CDC_CSCP_NoSpecificProtocol, 54 | 55 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 56 | 57 | .VendorID = 0x1209, 58 | .ProductID = 0x1776, 59 | .ReleaseNumber = VERSION_BCD(0,0,1), 60 | 61 | .ManufacturerStrIndex = STRING_ID_Manufacturer, 62 | .ProductStrIndex = STRING_ID_Product, 63 | .SerialNumStrIndex = USE_INTERNAL_SERIAL, 64 | 65 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 66 | }; 67 | 68 | /** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage 69 | * of the device in one of its supported configurations, including information about any device interfaces 70 | * and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting 71 | * a configuration so that the host may correctly communicate with the USB device. 72 | */ 73 | const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = 74 | { 75 | .Config = 76 | { 77 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 78 | 79 | .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), 80 | .TotalInterfaces = 2, 81 | 82 | .ConfigurationNumber = 1, 83 | .ConfigurationStrIndex = NO_DESCRIPTOR, 84 | 85 | .ConfigAttributes = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED), 86 | 87 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) 88 | }, 89 | 90 | .CDC_CCI_Interface = 91 | { 92 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 93 | 94 | .InterfaceNumber = INTERFACE_ID_CDC_CCI, 95 | .AlternateSetting = 0, 96 | 97 | .TotalEndpoints = 1, 98 | 99 | .Class = CDC_CSCP_CDCClass, 100 | .SubClass = CDC_CSCP_ACMSubclass, 101 | .Protocol = CDC_CSCP_VendorSpecificProtocol, 102 | 103 | .InterfaceStrIndex = NO_DESCRIPTOR 104 | }, 105 | 106 | .CDC_Functional_Header = 107 | { 108 | .Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalHeader_t), .Type = CDC_DTYPE_CSInterface}, 109 | .Subtype = CDC_DSUBTYPE_CSInterface_Header, 110 | 111 | .CDCSpecification = VERSION_BCD(1,1,0), 112 | }, 113 | 114 | .CDC_Functional_ACM = 115 | { 116 | .Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalACM_t), .Type = CDC_DTYPE_CSInterface}, 117 | .Subtype = CDC_DSUBTYPE_CSInterface_ACM, 118 | 119 | .Capabilities = 0x06, 120 | }, 121 | 122 | .CDC_Functional_Union = 123 | { 124 | .Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalUnion_t), .Type = CDC_DTYPE_CSInterface}, 125 | .Subtype = CDC_DSUBTYPE_CSInterface_Union, 126 | 127 | .MasterInterfaceNumber = INTERFACE_ID_CDC_CCI, 128 | .SlaveInterfaceNumber = INTERFACE_ID_CDC_DCI, 129 | }, 130 | 131 | .CDC_NotificationEndpoint = 132 | { 133 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 134 | 135 | .EndpointAddress = CDC_NOTIFICATION_EPADDR, 136 | .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 137 | .EndpointSize = CDC_NOTIFICATION_EPSIZE, 138 | .PollingIntervalMS = 0xFF 139 | }, 140 | 141 | .CDC_DCI_Interface = 142 | { 143 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 144 | 145 | .InterfaceNumber = INTERFACE_ID_CDC_DCI, 146 | .AlternateSetting = 0, 147 | 148 | .TotalEndpoints = 2, 149 | 150 | .Class = CDC_CSCP_CDCDataClass, 151 | .SubClass = CDC_CSCP_NoDataSubclass, 152 | .Protocol = CDC_CSCP_NoDataProtocol, 153 | 154 | .InterfaceStrIndex = NO_DESCRIPTOR 155 | }, 156 | 157 | .CDC_DataOutEndpoint = 158 | { 159 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 160 | 161 | .EndpointAddress = CDC_RX_EPADDR, 162 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 163 | .EndpointSize = CDC_TXRX_EPSIZE, 164 | .PollingIntervalMS = 0x05 165 | }, 166 | 167 | .CDC_DataInEndpoint = 168 | { 169 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 170 | 171 | .EndpointAddress = CDC_TX_EPADDR, 172 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 173 | .EndpointSize = CDC_TXRX_EPSIZE, 174 | .PollingIntervalMS = 0x05 175 | } 176 | }; 177 | 178 | /** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests 179 | * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate 180 | * via the language ID table available at USB.org what languages the device supports for its string descriptors. 181 | */ 182 | const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); 183 | 184 | /** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable 185 | * form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 186 | * Descriptor. 187 | */ 188 | const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"System76"); 189 | 190 | /** Product descriptor string. This is a Unicode string containing the product's details in human readable form, 191 | * and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 192 | * Descriptor. 193 | */ 194 | const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"Io"); 195 | 196 | /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" 197 | * documentation) by the application code so that the address and size of a requested descriptor can be given 198 | * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function 199 | * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the 200 | * USB host. 201 | */ 202 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 203 | const uint16_t wIndex, 204 | const void** const DescriptorAddress) 205 | { 206 | const uint8_t DescriptorType = (wValue >> 8); 207 | const uint8_t DescriptorNumber = (wValue & 0xFF); 208 | 209 | const void* Address = NULL; 210 | uint16_t Size = NO_DESCRIPTOR; 211 | 212 | switch (DescriptorType) 213 | { 214 | case DTYPE_Device: 215 | Address = &DeviceDescriptor; 216 | Size = sizeof(USB_Descriptor_Device_t); 217 | break; 218 | case DTYPE_Configuration: 219 | Address = &ConfigurationDescriptor; 220 | Size = sizeof(USB_Descriptor_Configuration_t); 221 | break; 222 | case DTYPE_String: 223 | switch (DescriptorNumber) 224 | { 225 | case STRING_ID_Language: 226 | Address = &LanguageString; 227 | Size = pgm_read_byte(&LanguageString.Header.Size); 228 | break; 229 | case STRING_ID_Manufacturer: 230 | Address = &ManufacturerString; 231 | Size = pgm_read_byte(&ManufacturerString.Header.Size); 232 | break; 233 | case STRING_ID_Product: 234 | Address = &ProductString; 235 | Size = pgm_read_byte(&ProductString.Header.Size); 236 | break; 237 | } 238 | 239 | break; 240 | } 241 | 242 | *DescriptorAddress = Address; 243 | return Size; 244 | } 245 | -------------------------------------------------------------------------------- /usb/Descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for Descriptors.c. 34 | */ 35 | 36 | #ifndef _DESCRIPTORS_H_ 37 | #define _DESCRIPTORS_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | 42 | #include 43 | 44 | /* Macros: */ 45 | /** Endpoint address of the CDC device-to-host notification IN endpoint. */ 46 | #define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2) 47 | 48 | /** Endpoint address of the CDC device-to-host data IN endpoint. */ 49 | #define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3) 50 | 51 | /** Endpoint address of the CDC host-to-device data OUT endpoint. */ 52 | #define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4) 53 | 54 | /** Size in bytes of the CDC device-to-host notification IN endpoint. */ 55 | #define CDC_NOTIFICATION_EPSIZE 8 56 | 57 | /** Size in bytes of the CDC data IN and OUT endpoints. */ 58 | #define CDC_TXRX_EPSIZE 16 59 | 60 | /* Type Defines: */ 61 | /** Type define for the device configuration descriptor structure. This must be defined in the 62 | * application code, as the configuration descriptor contains several sub-descriptors which 63 | * vary between devices, and which describe the device's usage to the host. 64 | */ 65 | typedef struct 66 | { 67 | USB_Descriptor_Configuration_Header_t Config; 68 | 69 | // CDC Command Interface 70 | USB_Descriptor_Interface_t CDC_CCI_Interface; 71 | USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header; 72 | USB_CDC_Descriptor_FunctionalACM_t CDC_Functional_ACM; 73 | USB_CDC_Descriptor_FunctionalUnion_t CDC_Functional_Union; 74 | USB_Descriptor_Endpoint_t CDC_NotificationEndpoint; 75 | 76 | // CDC Data Interface 77 | USB_Descriptor_Interface_t CDC_DCI_Interface; 78 | USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; 79 | USB_Descriptor_Endpoint_t CDC_DataInEndpoint; 80 | } USB_Descriptor_Configuration_t; 81 | 82 | /** Enum for the device interface descriptor IDs within the device. Each interface descriptor 83 | * should have a unique ID index associated with it, which can be used to refer to the 84 | * interface from other descriptors. 85 | */ 86 | enum InterfaceDescriptors_t 87 | { 88 | INTERFACE_ID_CDC_CCI = 0, /**< CDC CCI interface descriptor ID */ 89 | INTERFACE_ID_CDC_DCI = 1, /**< CDC DCI interface descriptor ID */ 90 | }; 91 | 92 | /** Enum for the device string descriptor IDs within the device. Each string descriptor should 93 | * have a unique ID index associated with it, which can be used to refer to the string from 94 | * other descriptors. 95 | */ 96 | enum StringDescriptors_t 97 | { 98 | STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ 99 | STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ 100 | STRING_ID_Product = 2, /**< Product string ID */ 101 | }; 102 | 103 | /* Function Prototypes: */ 104 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 105 | const uint16_t wIndex, 106 | const void** const DescriptorAddress) 107 | ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 108 | 109 | #endif 110 | 111 | -------------------------------------------------------------------------------- /usb/LUFA USBtoSerial.inf: -------------------------------------------------------------------------------- 1 | ;************************************************************ 2 | ; Windows USB CDC ACM Setup File 3 | ; Copyright (c) 2000 Microsoft Corporation 4 | ;************************************************************ 5 | 6 | [DefaultInstall] 7 | CopyINF="LUFA USBtoSerial.inf" 8 | 9 | [Version] 10 | Signature="$Windows NT$" 11 | Class=Ports 12 | ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} 13 | Provider=%MFGNAME% 14 | DriverVer=7/1/2012,10.0.0.0 15 | 16 | [Manufacturer] 17 | %MFGNAME%=DeviceList, NTx86, NTamd64, NTia64 18 | 19 | [SourceDisksNames] 20 | 21 | [SourceDisksFiles] 22 | 23 | [DestinationDirs] 24 | DefaultDestDir=12 25 | 26 | [DriverInstall] 27 | Include=mdmcpq.inf 28 | CopyFiles=FakeModemCopyFileSection 29 | AddReg=DriverInstall.AddReg 30 | 31 | [DriverInstall.Services] 32 | Include=mdmcpq.inf 33 | AddService=usbser, 0x00000002, LowerFilter_Service_Inst 34 | 35 | [DriverInstall.AddReg] 36 | HKR,,EnumPropPages32,,"msports.dll,SerialPortPropPageProvider" 37 | 38 | ;------------------------------------------------------------------------------ 39 | ; Vendor and Product ID Definitions 40 | ;------------------------------------------------------------------------------ 41 | ; When developing your USB device, the VID and PID used in the PC side 42 | ; application program and the firmware on the microcontroller must match. 43 | ; Modify the below line to use your VID and PID. Use the format as shown below. 44 | ; Note: One INF file can be used for multiple devices with different VID and PIDs. 45 | ; For each supported device, append ",USB\VID_xxxx&PID_yyyy" to the end of the line. 46 | ;------------------------------------------------------------------------------ 47 | [DeviceList] 48 | %DESCRIPTION%=DriverInstall, USB\VID_03EB&PID_204B 49 | 50 | [DeviceList.NTx86] 51 | %DESCRIPTION%=DriverInstall, USB\VID_03EB&PID_204B 52 | 53 | [DeviceList.NTamd64] 54 | %DESCRIPTION%=DriverInstall, USB\VID_03EB&PID_204B 55 | 56 | [DeviceList.NTia64] 57 | %DESCRIPTION%=DriverInstall, USB\VID_03EB&PID_204B 58 | 59 | ;------------------------------------------------------------------------------ 60 | ; String Definitions 61 | ;------------------------------------------------------------------------------ 62 | ;Modify these strings to customize your device 63 | ;------------------------------------------------------------------------------ 64 | [Strings] 65 | MFGNAME="http://www.lufa-lib.org" 66 | DESCRIPTION="LUFA USB to Serial" -------------------------------------------------------------------------------- /usb/USBtoSerial.c: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | #include "USBtoSerial.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #include "Descriptors.h" 38 | #include 39 | #include 40 | 41 | /** LUFA CDC Class driver interface configuration and state information. This structure is 42 | * passed to all CDC Class driver functions, so that multiple instances of the same class 43 | * within a device can be differentiated from one another. 44 | */ 45 | USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = 46 | { 47 | .Config = 48 | { 49 | .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI, 50 | .DataINEndpoint = 51 | { 52 | .Address = CDC_TX_EPADDR, 53 | .Size = CDC_TXRX_EPSIZE, 54 | .Banks = 1, 55 | }, 56 | .DataOUTEndpoint = 57 | { 58 | .Address = CDC_RX_EPADDR, 59 | .Size = CDC_TXRX_EPSIZE, 60 | .Banks = 1, 61 | }, 62 | .NotificationEndpoint = 63 | { 64 | .Address = CDC_NOTIFICATION_EPADDR, 65 | .Size = CDC_NOTIFICATION_EPSIZE, 66 | .Banks = 1, 67 | }, 68 | }, 69 | }; 70 | 71 | #define FLASH_SIZE_BYTES 32768UL 72 | #define BOOTLOADER_SEC_SIZE_BYTES 4096UL 73 | 74 | /* From http://www.fourwalledcubicle.com/files/LUFA/Doc/170418/html/_page__software_bootloader_start.html { */ 75 | uint32_t Boot_Key ATTR_NO_INIT; 76 | #define MAGIC_BOOT_KEY 0xBADCAFE5 77 | #define BOOTLOADER_START_ADDRESS ((FLASH_SIZE_BYTES - BOOTLOADER_SEC_SIZE_BYTES) >> 1) 78 | void Bootloader_Jump_Check(void) ATTR_INIT_SECTION(3); 79 | void Bootloader_Jump_Check(void) 80 | { 81 | // If the reset source was the bootloader and the key is correct, clear it and jump to the bootloader 82 | if ((MCUSR & (1 << WDRF)) && (Boot_Key == MAGIC_BOOT_KEY)) 83 | { 84 | Boot_Key = 0; 85 | ((void (*)(void))BOOTLOADER_START_ADDRESS)(); 86 | } 87 | } 88 | void Jump_To_Bootloader(void) 89 | { 90 | // If USB is used, detach from the bus and reset it 91 | USB_Disable(); 92 | // Disable all interrupts 93 | cli(); 94 | // Wait two seconds for the USB detachment to register on the host 95 | Delay_MS(2000); 96 | // Set the bootloader key to the magic value and force a reset 97 | Boot_Key = MAGIC_BOOT_KEY; 98 | wdt_enable(WDTO_250MS); 99 | for (;;); 100 | } 101 | /* } */ 102 | 103 | void USBtoSerial_Bootloader(void) { 104 | Jump_To_Bootloader(); 105 | } 106 | 107 | void USBtoSerial_Init(void) { 108 | #if (ARCH == ARCH_AVR8) 109 | /* Disable watchdog if enabled by bootloader/fuses */ 110 | MCUSR &= ~(1 << WDRF); 111 | wdt_disable(); 112 | 113 | /* Disable clock division */ 114 | clock_prescale_set(clock_div_1); 115 | #endif 116 | 117 | GlobalInterruptEnable(); 118 | USB_Init(); 119 | } 120 | 121 | int16_t USBtoSerial_Read(void) { 122 | return CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface); 123 | } 124 | 125 | uint8_t USBtoSerial_Write(uint8_t byte) { 126 | return CDC_Device_SendByte(&VirtualSerial_CDC_Interface, byte); 127 | } 128 | 129 | void USBtoSerial_Task(void) { 130 | CDC_Device_USBTask(&VirtualSerial_CDC_Interface); 131 | USB_USBTask(); 132 | } 133 | 134 | void USBtoSerial_Destroy(void) { 135 | USB_Disable(); 136 | } 137 | 138 | void EVENT_USB_Device_ConfigurationChanged(void) { 139 | CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface); 140 | } 141 | 142 | void EVENT_USB_Device_ControlRequest(void) { 143 | CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface); 144 | } 145 | 146 | /** Event handler for the CDC Class driver Line Encoding Changed event. 147 | * 148 | * \param[in] CDCInterfaceInfo Pointer to the CDC class interface configuration structure being referenced 149 | */ 150 | void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo) {} 151 | -------------------------------------------------------------------------------- /usb/USBtoSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2018. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | #ifndef _USB_SERIAL_H_ 32 | #define _USB_SERIAL_H_ 33 | #include 34 | 35 | void USBtoSerial_Bootloader(void); 36 | 37 | void USBtoSerial_Init(void); 38 | int16_t USBtoSerial_Read(void); 39 | uint8_t USBtoSerial_Write(uint8_t byte); 40 | void USBtoSerial_Task(void); 41 | void USBtoSerial_Destroy(void); 42 | #endif 43 | -------------------------------------------------------------------------------- /usb/USBtoSerial.txt: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3 | * This file contains special DoxyGen information for the generation of the main page and other special 4 | * documentation pages. It is not a project source file. 5 | */ 6 | 7 | /** \mainpage USB to Serial Converter Project 8 | * 9 | * \section Sec_Compat Project Compatibility: 10 | * 11 | * The following list indicates what microcontrollers are compatible with this project. 12 | * 13 | * \li Series 7 USB AVRs (AT90USBxxx7) 14 | * \li Series 6 USB AVRs (AT90USBxxx6) 15 | * \li Series 4 USB AVRs (ATMEGAxxU4) 16 | * \li Series 2 USB AVRs (AT90USBxx2, ATMEGAxxU2) 17 | * 18 | * \section Sec_Info USB Information: 19 | * 20 | * The following table gives a rundown of the USB utilization of this project. 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 42 | * 43 | *
USB Mode:Device
USB Class:Communications Device Class (CDC)
USB Subclass:Abstract Control Model (ACM)
Relevant Standards:USBIF CDC Class Standard
Supported USB Speeds:Full Speed Mode
44 | * 45 | * \section Sec_Description Project Description: 46 | * 47 | * USB to Serial bridge project. This project allows a USB AVR to serve 48 | * as a USB to USART bridge between a USB host and a device lacking a 49 | * USB port. When programmed into a USB AVR, the AVR will enumerate as a 50 | * virtual COM port. 51 | * 52 | * The AVR's hardware USART's settings will change to mirror as closely as 53 | * possible the serial settings set on the host. However, due to hardware 54 | * limitations, some options may not be supported (baud rates with unacceptable 55 | * error rates at the AVR's clock speed, data lengths other than 6, 7 or 8 bits, 56 | * 1.5 stop bits, parity other than none, even or odd). 57 | * 58 | * After running this project for the first time on a new computer, 59 | * you will need to supply the .INF file located in this project 60 | * project's directory as the device's driver when running under 61 | * Windows. This will enable Windows to use its inbuilt CDC drivers, 62 | * negating the need for custom drivers for the device. Other 63 | * Operating Systems should automatically use their own inbuilt 64 | * CDC-ACM drivers. 65 | * 66 | * \section Sec_Options Project Options 67 | * 68 | * The following defines can be found in this project, which can control the project behaviour when defined, or changed in value. 69 | * 70 | * 71 | * 72 | * 75 | * 76 | *
73 | * None 74 | *
77 | */ 78 | 79 | -------------------------------------------------------------------------------- /usb/asf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | USB to Serial USART converter project. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /usb/makefile: -------------------------------------------------------------------------------- 1 | # 2 | # LUFA Library 3 | # Copyright (C) Dean Camera, 2018. 4 | # 5 | # dean [at] fourwalledcubicle [dot] com 6 | # www.lufa-lib.org 7 | # 8 | # -------------------------------------- 9 | # LUFA Project Makefile. 10 | # -------------------------------------- 11 | 12 | # Run "make help" for target help. 13 | 14 | MCU = atmega32u4 15 | ARCH = AVR8 16 | BOARD = NONE 17 | F_CPU = 16000000 18 | F_USB = $(F_CPU) 19 | OPTIMIZATION = s 20 | TARGET = USBtoSerial 21 | SRC = $(TARGET).c Descriptors.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) 22 | LUFA_PATH = ../lufa/LUFA 23 | CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/ 24 | LD_FLAGS = 25 | 26 | AVRDUDE_PROGRAMMER = avr109 27 | AVRDUDE_PORT = /dev/ttyACM0 28 | 29 | # Default target 30 | all: 31 | 32 | # Include LUFA-specific DMBS extension modules 33 | DMBS_LUFA_PATH ?= $(LUFA_PATH)/Build/LUFA 34 | include $(DMBS_LUFA_PATH)/lufa-sources.mk 35 | include $(DMBS_LUFA_PATH)/lufa-gcc.mk 36 | 37 | # Include common DMBS build system modules 38 | DMBS_PATH ?= $(LUFA_PATH)/Build/DMBS/DMBS 39 | include $(DMBS_PATH)/core.mk 40 | include $(DMBS_PATH)/cppcheck.mk 41 | include $(DMBS_PATH)/doxygen.mk 42 | include $(DMBS_PATH)/dfu.mk 43 | include $(DMBS_PATH)/gcc.mk 44 | include $(DMBS_PATH)/hid.mk 45 | include $(DMBS_PATH)/avrdude.mk 46 | include $(DMBS_PATH)/atprogram.mk 47 | --------------------------------------------------------------------------------