From 9f7159e58772412137e73c56b10189a3753f7799 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 17 Jun 2016 22:53:54 +0200 Subject: [PATCH 1/2] Allow loading an OSCCAL value from EEPROM When an EEPROM address is given through OSCCAL_EEPROM_ADDR at compiletime, a byte is read from that address on startup and written to OSCCAL (unless it is 0xff, which should never be a meaningful calibration value). This allows improving the accuracy of the UART baudrate when running off the internal oscillator. Enabling this option adds 30 bytes on the atmega328p build. This can probably be reduced by inlining eeprom_read_byte. --- optiboot/bootloaders/optiboot/Makefile | 6 ++++++ optiboot/bootloaders/optiboot/optiboot.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/optiboot/bootloaders/optiboot/Makefile b/optiboot/bootloaders/optiboot/Makefile index a886411e81..3dc1feabc4 100644 --- a/optiboot/bootloaders/optiboot/Makefile +++ b/optiboot/bootloaders/optiboot/Makefile @@ -190,8 +190,14 @@ ifdef SINGLESPEED SS_CMD = -DSINGLESPEED=1 endif +ifdef OSCCAL_EEPROM_ADDR +OSCCAL_CMD = -DOSCCAL_EEPROM_ADDR=$(OSCCAL_EEPROM_ADDR) +dummy = FORCE +endif + COMMON_OPTIONS = $(BAUD_RATE_CMD) $(LED_START_FLASHES_CMD) $(BIGBOOT_CMD) COMMON_OPTIONS += $(SOFT_UART_CMD) $(LED_DATA_FLASH_CMD) $(LED_CMD) $(SS_CMD) +COMMON_OPTIONS += $(OSCCAL_CMD) #UART is handled separately and only passed for devices with more than one. ifdef UART diff --git a/optiboot/bootloaders/optiboot/optiboot.c b/optiboot/bootloaders/optiboot/optiboot.c index df807ab13a..6559d49646 100644 --- a/optiboot/bootloaders/optiboot/optiboot.c +++ b/optiboot/bootloaders/optiboot/optiboot.c @@ -128,6 +128,11 @@ /* UART number (0..n) for devices with more than */ /* one hardware uart (644P, 1284P, etc) */ /* */ +/* OSCCAL_EEPROM_ADDR */ +/* On startup, load an oscillator calibration value from */ +/* this address in EEPROM and write it to OSCCAL (unless */ +/* it is 0xff). */ +/* */ /**********************************************************/ /**********************************************************/ @@ -468,6 +473,13 @@ int main(void) { SP=RAMEND; // This is done by hardware reset #endif +#if defined(OSCCAL_EEPROM_ADDR) + // Load OSCCAL before app start, so the app does not have to + ch = eeprom_read_byte((uint8_t*)OSCCAL_EEPROM_ADDR); + if (ch != 0xff) + OSCCAL = ch; +#endif + /* * modified Adaboot no-wait mod. * Pass the reset reason to app. Also, it appears that an Uno poweron From 8d68385a8a9a86838558e5c2f024d637f545c797 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 13 Sep 2016 21:43:06 +0200 Subject: [PATCH 2/2] Allow loading an OSCCAL value from PROGMEM When the OSCCAL_PROGMEM variable is given at compiletime, a byte is allocated in PROGMEM (flash) at a fixed location at the top of the flash space (just below the version bytes, so the third byte from the top). If this byte is overwritten with a calibration value (e.g. when programming the bootloader), it will be automatically loaded on startup. This allows improving the accuracy of the UART baudrate when running off the internal oscillator, and prevents the actual program from having to worry about calibration. The default value of this byte is 0xff, which causes it to be ignored. To fix the address of this variable, a dedicated .osccal section is used for it, and the linker is passed some options to put it in the right place (identical to what happens for the version variable). Enabling this option adds 15 bytes on the atmega328p build. --- optiboot/bootloaders/optiboot/Makefile | 23 ++++++++++++++--------- optiboot/bootloaders/optiboot/optiboot.c | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/optiboot/bootloaders/optiboot/Makefile b/optiboot/bootloaders/optiboot/Makefile index 3dc1feabc4..e9bb265345 100644 --- a/optiboot/bootloaders/optiboot/Makefile +++ b/optiboot/bootloaders/optiboot/Makefile @@ -59,7 +59,7 @@ export # defaults MCU_TARGET = atmega168 -LDSECTIONS = -Wl,--section-start=.text=0x3e00 -Wl,--section-start=.version=0x3ffe +LDSECTIONS = -Wl,--section-start=.text=0x3e00 -Wl,--section-start=.osccal=0x3ffd -Wl,--section-start=.version=0x3ffe # Build environments # Start of some ugly makefile-isms to allow optiboot to be built @@ -195,6 +195,11 @@ OSCCAL_CMD = -DOSCCAL_EEPROM_ADDR=$(OSCCAL_EEPROM_ADDR) dummy = FORCE endif +ifdef OSCCAL_PROGMEM +OSCCAL_CMD = -DOSCCAL_PROGMEM +dummy = FORCE +endif + COMMON_OPTIONS = $(BAUD_RATE_CMD) $(LED_START_FLASHES_CMD) $(BIGBOOT_CMD) COMMON_OPTIONS += $(SOFT_UART_CMD) $(LED_DATA_FLASH_CMD) $(LED_CMD) $(SS_CMD) COMMON_OPTIONS += $(OSCCAL_CMD) @@ -246,7 +251,7 @@ virboot8: TARGET = atmega8 virboot8: MCU_TARGET = atmega8 virboot8: CFLAGS += $(COMMON_OPTIONS) '-DVIRTUAL_BOOT_PARTITION' '-Dsave_vect_num=EE_RDY_vect_num' virboot8: AVR_FREQ ?= 16000000L -virboot8: LDSECTIONS = -Wl,--section-start=.text=0x1c00 -Wl,--section-start=.version=0x1ffe +virboot8: LDSECTIONS = -Wl,--section-start=.text=0x1c00 -Wl,--section-start=.osccal=0x1ffd -Wl,--section-start=.version=0x1ffe virboot8: $(PROGRAM)_virboot8.hex virboot8: $(PROGRAM)_virboot8.lst @@ -255,7 +260,7 @@ virboot328: TARGET = atmega328 virboot328: MCU_TARGET = atmega328p virboot328: CFLAGS += $(COMMON_OPTIONS) '-DVIRTUAL_BOOT_PARTITION' virboot328: AVR_FREQ ?= 16000000L -virboot328: LDSECTIONS = -Wl,--section-start=.text=0x7d80 -Wl,--section-start=.version=0x7ffe +virboot328: LDSECTIONS = -Wl,--section-start=.text=0x7d80 -Wl,--section-start=.osccal=0x7ffd -Wl,--section-start=.version=0x7ffe virboot328: $(PROGRAM)_virboot328.hex virboot328: $(PROGRAM)_virboot328.lst @@ -303,7 +308,7 @@ atmega328: TARGET = atmega328 atmega328: MCU_TARGET = atmega328p atmega328: CFLAGS += $(COMMON_OPTIONS) atmega328: AVR_FREQ ?= 16000000L -atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe +atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.osccal=0x7ffd -Wl,--section-start=.version=0x7ffe atmega328: $(PROGRAM)_atmega328.hex atmega328: $(PROGRAM)_atmega328.lst @@ -322,7 +327,7 @@ atmega328_isp: isp atmega1280: MCU_TARGET = atmega1280 atmega1280: CFLAGS += $(COMMON_OPTIONS) -DBIGBOOT $(UART_CMD) atmega1280: AVR_FREQ ?= 16000000L -atmega1280: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe +atmega1280: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.osccal=0x1fffd -Wl,--section-start=.version=0x1fffe atmega1280: $(PROGRAM)_atmega1280.hex atmega1280: $(PROGRAM)_atmega1280.lst @@ -333,7 +338,7 @@ atmega8: TARGET = atmega8 atmega8: MCU_TARGET = atmega8 atmega8: CFLAGS += $(COMMON_OPTIONS) atmega8: AVR_FREQ ?= 16000000L -atmega8: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe -Wl,--gc-sections -Wl,--undefined=optiboot_version +atmega8: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.osccal=0x1ffd -Wl,--section-start=.version=0x1ffe -Wl,--gc-sections -Wl,--undefined=optiboot_version atmega8: $(PROGRAM)_atmega8.hex atmega8: $(PROGRAM)_atmega8.lst @@ -544,10 +549,10 @@ clean: $(OBJDUMP) -h -S $< > $@ %.hex: %.elf - $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex $< $@ + $(OBJCOPY) -j .text -j .data -j .version -j .osccal --set-section-flags .version=alloc,load --set-section-flags .osccal=alloc,load -O ihex $< $@ %.srec: %.elf - $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O srec $< $@ + $(OBJCOPY) -j .text -j .data -j .version -j .osccal -set-section-flags .version=alloc,load --set-section-flags .osccal=alloc,load -O srec $< $@ %.bin: %.elf - $(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O binary $< $@ + $(OBJCOPY) -j .text -j .data -j .version -j .osccal --set-section-flags .version=alloc,load --set-section-flags .osccal=alloc,load -O binary $< $@ diff --git a/optiboot/bootloaders/optiboot/optiboot.c b/optiboot/bootloaders/optiboot/optiboot.c index 6559d49646..fb2beb32c9 100644 --- a/optiboot/bootloaders/optiboot/optiboot.c +++ b/optiboot/bootloaders/optiboot/optiboot.c @@ -133,6 +133,13 @@ /* this address in EEPROM and write it to OSCCAL (unless */ /* it is 0xff). */ /* */ +/* OSCCAL_PROGMEM */ +/* On startup, load an oscillator calibration value from */ +/* the top of the flash memory (unless it is 0xff, which */ +/* is the default). This byte is put into its own .osccal */ +/* section, so its address should be set through the */ +/* linker. */ +/* */ /**********************************************************/ /**********************************************************/ @@ -244,6 +251,11 @@ unsigned const int __attribute__((section(".version"))) optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER; +#if defined(OSCCAL_PROGMEM) +unsigned const char __attribute__((section(".osccal"))) +optiboot_osccal = 0xff; +#endif + #include #include @@ -480,6 +492,12 @@ int main(void) { OSCCAL = ch; #endif +#if defined(OSCCAL_PROGMEM) + // Load OSCCAL before app start, so the app does not have to + ch = pgm_read_byte(&optiboot_osccal); + if (ch != 0xff) + OSCCAL = ch; +#endif /* * modified Adaboot no-wait mod. * Pass the reset reason to app. Also, it appears that an Uno poweron