@@ -0,0 +1,307 @@
|
||||
/*
|
||||
MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org)
|
||||
Code based on OTA bootloader by ToSa
|
||||
Optimized and extended by tekka
|
||||
Version 1.0 / 20150206
|
||||
Size: 1834 bytes
|
||||
|
||||
Tested with Atmega328P, 16Mhz 3.3V and 5V and MYSController 0.1.2.266 (goo.gl/9DCWNo)
|
||||
|
||||
MCU: atmega328
|
||||
clock: 16Mhz (prescaler: off/1)
|
||||
bootsz: 1024W
|
||||
|
||||
|
||||
fuses for ISP:
|
||||
EX = 0xFE (use 0x06 for Arduino IDE, boards.txt)
|
||||
HI = 0xDA
|
||||
LO = 0xF7
|
||||
|
||||
nRF24L01+ connected to pins:
|
||||
CE = 9
|
||||
CSN = 10
|
||||
|
||||
Successfully tested with:
|
||||
|
||||
16Mhz extXTAL, 3.3V
|
||||
1Mhz intRC (8Mhz + DIV8), 3.3V
|
||||
128kHz intRC, 3.3V
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "MYSBootloader.h"
|
||||
|
||||
|
||||
#define MYSBOOTLOADER_MAJVER 1
|
||||
#define MYSBOOTLOADER_MINVER 0
|
||||
|
||||
#define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER)
|
||||
|
||||
//#define USE_PRESCALER
|
||||
#define MAX_RESEND 5
|
||||
|
||||
|
||||
// procedures and functions
|
||||
|
||||
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
|
||||
static uint16_t calcCRCrom (const void* ptr, uint16_t len);
|
||||
static uint8_t IsFirmwareValid();
|
||||
static void reboot();
|
||||
static void startup();
|
||||
static void programPage(uint32_t page, uint8_t *buf);
|
||||
static boolean sendWrite(MyMessage message);
|
||||
static bool sendAndWait(uint8_t reqType, uint8_t resType);
|
||||
|
||||
|
||||
static uint16_t calcCRCrom (const void* ptr, uint16_t len) {
|
||||
// init 0xFFFF
|
||||
uint16_t crc = ~0;
|
||||
for (uint16_t i = 0; i < len; ++i) {
|
||||
crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i));
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint8_t IsFirmwareValid () {
|
||||
return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc;
|
||||
}
|
||||
|
||||
static void reboot() {
|
||||
// any pending eeprom activities?
|
||||
eeprom_busy_wait();
|
||||
// trigger watchdog ASAP
|
||||
watchdogConfig(WATCHDOG_16MS);
|
||||
// wait until WD triggers
|
||||
while (1);
|
||||
}
|
||||
|
||||
static void startup() {
|
||||
if (IsFirmwareValid()) {
|
||||
// WD off
|
||||
watchdogConfig(WATCHDOG_OFF);
|
||||
|
||||
#ifdef USE_PRESCALER
|
||||
// reset prescaler
|
||||
clock_prescale_set(orgClockDiv);
|
||||
#endif
|
||||
|
||||
// start sketch
|
||||
((void(*)()) 0)();
|
||||
|
||||
} else {
|
||||
reboot();
|
||||
}
|
||||
}
|
||||
|
||||
static void programPage(uint32_t page, uint8_t *buf) {
|
||||
// using out commands, saves some bytes :)
|
||||
__boot_page_erase_short(page);
|
||||
boot_spm_busy_wait();
|
||||
for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) {
|
||||
uint16_t w = *buf++;
|
||||
// generate word
|
||||
w += (*buf++) << 8;
|
||||
__boot_page_fill_short(page + i, w);
|
||||
}
|
||||
__boot_page_write_short(page);
|
||||
boot_spm_busy_wait();
|
||||
__boot_rww_enable_short();
|
||||
}
|
||||
|
||||
|
||||
static boolean sendWrite(MyMessage message) {
|
||||
return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS));
|
||||
}
|
||||
|
||||
static bool sendAndWait(uint8_t reqType, uint8_t resType) {
|
||||
outMsg.type = reqType;
|
||||
// outer loop, retries
|
||||
for (uint8_t i = 0; i < MAX_RESEND; i++) {
|
||||
sendWrite(outMsg);
|
||||
// loop 20 times, wait time 0.1ms if no/wrong data => 2s
|
||||
for (uint8_t j = 0; j < 20; j++) {
|
||||
// loop 100 times, wait 1ms if no/wrong data => 0.1s
|
||||
for (uint8_t k = 0; k < 100; k++) {
|
||||
watchdogReset();
|
||||
// Tx FIFO data available? (we don't care about the pipe here)
|
||||
if (available(NULL)) {
|
||||
// read message from FIFO
|
||||
readMessage(inMsg.array);
|
||||
// protocol compatible? if not ignore msg
|
||||
if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) {
|
||||
continue;
|
||||
}
|
||||
if (inMsg.destination == nc.nodeId) {
|
||||
// msg for us
|
||||
if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) {
|
||||
if (inMsg.bValue < nc.distance - 1) {
|
||||
// got new routing info, update settings
|
||||
nc.distance = inMsg.bValue + 1;
|
||||
nc.parentNodeId = inMsg.sender;
|
||||
}
|
||||
}
|
||||
// did we receive expected reply?
|
||||
if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// wait 1ms if no data available
|
||||
delaym(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// main start
|
||||
int main(void) {
|
||||
|
||||
asm volatile ("clr __zero_reg__");
|
||||
// reset MCU status register
|
||||
MCUSR = 0;
|
||||
|
||||
#ifdef USE_PRESCALER
|
||||
// get current prescale
|
||||
orgClockDiv = clock_prescale_get();
|
||||
//switch to 4 MHz on 16Mhz crystal
|
||||
clock_prescale_set(F_CPU_DIV);
|
||||
#endif
|
||||
|
||||
// enable watchdog to avoid deadlock
|
||||
watchdogConfig(WATCHDOG_8S);
|
||||
|
||||
// initialize SPI
|
||||
SPIinit();
|
||||
|
||||
// initialize RF module
|
||||
RFinit();
|
||||
|
||||
// Read node config from EEPROM, i.e. nodeId, parent nodeId, distance
|
||||
eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig));
|
||||
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
|
||||
eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
|
||||
|
||||
// bootloader should find closest node during each reboot
|
||||
|
||||
// invalidate parent node settings, since we have to re-discover them for every single reboot
|
||||
nc.distance = 0xFF;
|
||||
nc.parentNodeId = 0xFF;
|
||||
|
||||
// prepare for I_FIND_PARENTS
|
||||
outMsg.sender = nc.nodeId;
|
||||
outMsg.last = nc.nodeId;
|
||||
outMsg.sensor = 0xFF;
|
||||
outMsg.destination = BROADCAST_ADDRESS;
|
||||
|
||||
mSetVersion(outMsg, PROTOCOL_VERSION);
|
||||
mSetLength(outMsg, 0);
|
||||
mSetCommand(outMsg, C_INTERNAL);
|
||||
mSetAck(outMsg,false);
|
||||
mSetPayloadType(outMsg, P_STRING);
|
||||
|
||||
// set reading & writing pipe address
|
||||
setAddress(nc.nodeId);
|
||||
|
||||
// network up?, get neighbors, else startup
|
||||
if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) {
|
||||
startup();
|
||||
}
|
||||
|
||||
// all messages to gateway
|
||||
outMsg.destination = GATEWAY_ADDRESS;
|
||||
|
||||
// if no node id assigned, request new id
|
||||
if (nc.nodeId == AUTO) {
|
||||
// listen to broadcast
|
||||
openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS));
|
||||
if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) {
|
||||
// save id to eeprom
|
||||
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data));
|
||||
}
|
||||
// we could go on and set everything right here, but rebooting will take care of that - saves bytes :)
|
||||
reboot();
|
||||
}
|
||||
|
||||
// wuff
|
||||
watchdogReset();
|
||||
// prepare for FW config request
|
||||
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)outMsg.data;
|
||||
mSetLength(outMsg, sizeof(RequestFirmwareConfig));
|
||||
mSetCommand(outMsg, C_STREAM);
|
||||
mSetPayloadType(outMsg,P_CUSTOM);
|
||||
// copy node settings to reqFWConfig
|
||||
memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig));
|
||||
// add bootloader information
|
||||
reqFWConfig->BLVersion = MYSBOOTLOADER_VERSION;
|
||||
|
||||
// send node config and request FW config from controller
|
||||
if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE)) {
|
||||
startup();
|
||||
}
|
||||
|
||||
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)inMsg.data;
|
||||
|
||||
// compare with current node configuration, if equal startup
|
||||
if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
|
||||
startup();
|
||||
}
|
||||
|
||||
// *********** from here on we will fetch new FW
|
||||
|
||||
// invalidate current CRC
|
||||
fc.crc = 0xFFFF;
|
||||
// write fetched type and version in case OTA fails (BL will reboot and re-request FW with stored settings)
|
||||
eeprom_write_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig));
|
||||
|
||||
// copy new FW config
|
||||
memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
|
||||
RequestFWBlock *firmwareRequest = (RequestFWBlock *)outMsg.data;
|
||||
mSetLength(outMsg, sizeof(RequestFWBlock));
|
||||
|
||||
firmwareRequest->type = fc.type;
|
||||
firmwareRequest->version = fc.version;
|
||||
|
||||
// request FW from controller
|
||||
for (uint16_t block = fc.blocks; block > 0; block--) {
|
||||
firmwareRequest->block = (block - 1);
|
||||
// request FW block
|
||||
if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) {
|
||||
reboot();
|
||||
}
|
||||
|
||||
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)inMsg.data;
|
||||
// calculate page offset
|
||||
uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE;
|
||||
// write to buffer
|
||||
memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
|
||||
// program if page full
|
||||
if (offset == 0) {
|
||||
programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf);
|
||||
}
|
||||
}
|
||||
|
||||
// wuff
|
||||
watchdogReset();
|
||||
|
||||
// all blocks transmitted, calc CRC and write to eeprom if valid
|
||||
if (IsFirmwareValid()) {
|
||||
// if FW is valid, write settings to eeprom
|
||||
eeprom_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
|
||||
// we could restart directly here, but eeprom may still be busy, thus reboot (we check for idle eeprom before rebooting) and save some bytes :)
|
||||
}
|
||||
// final step
|
||||
reboot();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef MyOtaBootloader_H
|
||||
#define MyOtaBootloader_H
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <util/crc16.h>
|
||||
|
||||
#include "MyMessage.h"
|
||||
#include "MySensor.h"
|
||||
#include "boot.h"
|
||||
#include "MYSBootloaderRF24.h"
|
||||
|
||||
|
||||
#define FIRMWARE_BLOCK_SIZE 16
|
||||
|
||||
// FW config structure, stored in eeprom
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t version;
|
||||
uint16_t blocks;
|
||||
uint16_t crc;
|
||||
} __attribute__((packed)) NodeFirmwareConfig;
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t version;
|
||||
uint16_t blocks;
|
||||
uint16_t crc;
|
||||
uint16_t BLVersion;
|
||||
} __attribute__((packed)) RequestFirmwareConfig;
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t version;
|
||||
uint16_t block;
|
||||
} __attribute__((packed)) RequestFWBlock;
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
uint16_t version;
|
||||
uint16_t block;
|
||||
uint8_t data[FIRMWARE_BLOCK_SIZE];
|
||||
} __attribute__((packed)) ReplyFWBlock;
|
||||
|
||||
static struct NodeConfig nc;
|
||||
static NodeFirmwareConfig fc;
|
||||
static MyMessage outMsg;
|
||||
static MyMessage inMsg;
|
||||
|
||||
#ifdef PRESCALE
|
||||
static clock_div_t orgClockDiv = 0;
|
||||
#endif
|
||||
|
||||
static uint8_t progBuf[SPM_PAGESIZE];
|
||||
|
||||
#endif // MyOtaBootloader_H
|
||||
@@ -0,0 +1,117 @@
|
||||
:1078000021E0A0E0B1E001C01D92A53DB207E1F783
|
||||
:10781000CDB7DEB76897DEBFCDBF112414BE88E1B7
|
||||
:107820008093600089E2809360008FEE84B963E00A
|
||||
:1078300083E201D26FE584E2FED16BE381E2FBD10A
|
||||
:107840006CE485E2F8D167E286E2F5D10DD263E718
|
||||
:1078500080E5F1D166E08DE3EED16FE38CE3EBD10F
|
||||
:107860006EEF80E2E8D185E053D143E050E060E084
|
||||
:1078700070E08AEC91E032D348E050E06BE171E0D7
|
||||
:1078800082EC91E02BD38FEF8093CC018093CB01DE
|
||||
:107890002091CA012093A2012093A1018093A70106
|
||||
:1078A0008093A30182E08093A4018091A501887058
|
||||
:1078B00083608093A5012F3FD1F130E040E050E09C
|
||||
:1078C00060E070E080E090E034504E4157456745FD
|
||||
:1078D0007F4F8F4F9F4F29013A014B015C01298B4C
|
||||
:1078E0005A8A4B8B7C8A6D8B9E8A8F8BB88E85E0F3
|
||||
:1078F000FE017196ADECB1E001900D928A95E1F731
|
||||
:1079000045E0BE016F5E7F4F8AE27FD149865A868D
|
||||
:107910006B867C868D869E86AF86B88A45E0BE01E2
|
||||
:10792000675F7F4F8BE271D163E082E284D168E0D0
|
||||
:1079300087E09FD1811101C049D11092A3018091AC
|
||||
:10794000CA018F3FF1F489838CEF8A8381EE8B83A8
|
||||
:1079500088EA8C838D831E821F82188645E0BE01D3
|
||||
:107960006F5F7F4F8BE251D164E083E082D1882347
|
||||
:1079700039F087E891E080D2682F80E090E0C7D2AC
|
||||
:107980001CD1A8958091A401877080658093A40183
|
||||
:107990008091A5018871846C8093A50188E0E2EC58
|
||||
:1079A000F1E0A8EAB1E001900D928A95E1F781E05B
|
||||
:1079B00090E09093B1018093B00161E080E059D1F3
|
||||
:1079C000811101C003D148E050E067E871E082EC2A
|
||||
:1079D00091E06FD2892B09F4F9D08FEF9FEF90934C
|
||||
:1079E000C9018093C80148E050E06BE171E082EC8E
|
||||
:1079F00091E084D288E0E7E8F1E0A2ECB1E0019008
|
||||
:107A00000D928A95E1F78091A4018770806380933D
|
||||
:107A1000A4018091C2019091C3019093A901809328
|
||||
:107A2000A8018091C4019091C5019093AB0180930E
|
||||
:107A3000AA01A090C601B090C70185010150104F66
|
||||
:107A400064E0000F111F6A95E1F770E1972E88241A
|
||||
:107A50008394E5E07E2EF1E16F2EA114B10409F4C8
|
||||
:107A60004AC081E0A81AB108B092AD01A092AC0161
|
||||
:107A700063E082E0FED0882309F482CF802F8077F4
|
||||
:107A8000A82FB0E0A050BF4FEDE8F1E0992D019094
|
||||
:107A90000D929A95E1F781112BC083E0F80187BF21
|
||||
:107AA000E89507B600FCFDCF4091000180E090E032
|
||||
:107AB000DC01A050BF4FFC01E00FF11F11962C918B
|
||||
:107AC000119730E0322F2227240F311D090187BE84
|
||||
:107AD000E895112412964C9102968038910541F751
|
||||
:107AE000F80177BEE89507B600FCFDCF67BEE895C4
|
||||
:107AF00000511109B2CFA8952CD0882309F440CFAA
|
||||
:107B000048E050E06BE171E082EC91E0F7D138CFD2
|
||||
:107B100090E0981751F02DE730E000002150310936
|
||||
:107B200021153105D1F79F5FF4CF089598E087FFC5
|
||||
:107B300002C02B9A01C02B9800002D9A000023B19F
|
||||
:107B4000880F22952170822B2D980000915079F793
|
||||
:107B500008958091C6019091C70124E0880F991F74
|
||||
:107B60002A95E1F7E0E0F0E02FEF3FEFE817F907A3
|
||||
:107B7000D1F044912427422F42954227042E469566
|
||||
:107B800046954025042E469540254770022E232F0A
|
||||
:107B9000469507944795302D242706944795302520
|
||||
:107BA00024273196E3CF81E04091C8015091C9016B
|
||||
:107BB0002417350709F080E00895F999FECF88E190
|
||||
:107BC0008093600088E080936000FFCFC2DF88234D
|
||||
:107BD00041F088E18093600010926000E0E0F0E006
|
||||
:107BE0000994EBDF1F93CF93DF93EB01142F2A98B7
|
||||
:107BF0009DDF1C0F1C1721F08FEF98DF8993FACFC0
|
||||
:107C00002A9ADF91CF911F9108951F93CF93DF930D
|
||||
:107C1000EB01142F2A988ADF1C0F1C1719F0899189
|
||||
:107C200085DFFBCF2A9ADF91CF911F91089541E024
|
||||
:107C300060E070E0D7CFCF93DF931F92CDB7DEB770
|
||||
:107C4000698341E0BE016F5F7F4FDFDF0F90DF91FF
|
||||
:107C5000CF91089540E060E070E082EED6CF40E042
|
||||
:107C600060E070E081EED1CFF5DFF9DF60E787E219
|
||||
:107C7000E2CF6F927F928F929F92AF92BF92CF92FC
|
||||
:107C8000DF92EF92FF920F931F93CF93DF93CDB7C5
|
||||
:107C9000DEB7AA97DEBFCDBF762E8093A60105E0A2
|
||||
:107CA00041E2642EE1EAF1E0DE011196862D0190B9
|
||||
:107CB0000D928A95E1F7FB801C8116951695169515
|
||||
:107CC000195F2091CB0129982AA7CEDF6EEF80E2C1
|
||||
:107CD000B2DF2AA530E040E050E060E070E080E0F4
|
||||
:107CE00090E034504E41574567457F4F8F4F9F4F2F
|
||||
:107CF0002AA33BA34CA35DA36EA37FA388A799A748
|
||||
:107D000045E0BE016E5D7F4F8AE27FDF45E0BE0148
|
||||
:107D10006E5D7F4F80E379DF8FEFF81202C080EB5A
|
||||
:107D200001C080EA412FBE016F5F7F4F6EDF299A4D
|
||||
:107D300081E0EEDE299840E060E070E08FEF52DFF6
|
||||
:107D40008073C9F360E787E276DF84FD88DF6FE048
|
||||
:107D500080E271DF89DF8090CD019090CE01A0900C
|
||||
:107D6000CF01B090D001C090D101D090D201E0906D
|
||||
:107D7000D301F090D4019401A501B601C701A0E0A0
|
||||
:107D80006FD029F045E06DEC71E08AE23EDF299A80
|
||||
:107D900014E134E6F32EA89587E149DF80FD4BC05E
|
||||
:107DA00080E645DF813210F055DF80E0482F60E843
|
||||
:107DB00071E081E617DF60E787E23DDF80918301B4
|
||||
:107DC0008770823059F5909182018091CA01981391
|
||||
:107DD00025C040918401842F87708330B1F4809155
|
||||
:107DE0008501883091F450918701252F30E08091F2
|
||||
:107DF000CC0190E00197281739073CF45F5F50935E
|
||||
:107E0000CC01809181018093CB018091A5018427D1
|
||||
:107E1000877021F480918501871559F0FA9409F053
|
||||
:107E2000BACF115009F0B5CF015009F03BCF80E037
|
||||
:107E300005C081E003C08AE06BDEF0CFAA96DEBF0A
|
||||
:107E4000CDBFDF91CF911F910F91FF90EF90DF9009
|
||||
:107E5000CF90BF90AF909F908F907F906F9008953C
|
||||
:107E60000024A7FD00942A17300540055005600541
|
||||
:107E70007005800590050895FC0188279927E894EE
|
||||
:107E800021912032E9F3293010F02E30C8F32B3243
|
||||
:107E900039F02D3231F4689403C018D0820F911D4F
|
||||
:107EA000219120532A30C8F31EF4909581959F4F5D
|
||||
:107EB0000895FB01DC0104C08D910190801921F42B
|
||||
:107EC00041505040C8F7881B990B08957AE0979F5E
|
||||
:107ED000902D879F802D910D11240895DC01CB01F9
|
||||
:107EE000FC01F999FECF06C0F2BDE1BDF89A3196CA
|
||||
:107EF00000B40D9241505040B8F70895DC01CB0119
|
||||
:107F000002C02D9105D041505040D8F70895262F3A
|
||||
:107F1000F999FECF1FBA92BD81BD20BD0FB6F8946E
|
||||
:0A7F2000FA9AF99A0FBE019608952F
|
||||
:040000030000780081
|
||||
:00000001FF
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifndef MYSBootloaderHW_H
|
||||
#define MYSBootloaderHW_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// hardware
|
||||
|
||||
|
||||
|
||||
#ifdef USE_PRESCALER
|
||||
#define F_CPU_DIV clock_div_4
|
||||
#else
|
||||
#define F_CPU_DIV clock_div_1
|
||||
#endif
|
||||
|
||||
#define F_CPU_REAL F_CPU / (1 << F_CPU_DIV)
|
||||
#define DELAY_M 125 * (1 << F_CPU_DIV)
|
||||
#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 )
|
||||
#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1)))
|
||||
#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE)
|
||||
#define UART_SRA UCSR0A
|
||||
#define UART_SRB UCSR0B
|
||||
#define UART_SRC UCSR0C
|
||||
#define UART_SRL UBRR0L
|
||||
#define UART_UDR UDR0
|
||||
|
||||
// boolean definition
|
||||
|
||||
typedef uint8_t bool;
|
||||
typedef uint8_t boolean;
|
||||
#define false 0x0
|
||||
#define true 0x1
|
||||
#define LOW 0x0
|
||||
#define HIGH 0x1
|
||||
|
||||
// Watchdog definitions and functions
|
||||
|
||||
#define WATCHDOG_OFF (0)
|
||||
#define WATCHDOG_16MS (_BV(WDE))
|
||||
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
|
||||
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
|
||||
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
||||
|
||||
inline void watchdogReset() {
|
||||
asm volatile ("wdr");
|
||||
}
|
||||
|
||||
void watchdogConfig(uint8_t x) {
|
||||
WDTCSR = _BV(WDCE) | _BV(WDE);
|
||||
WDTCSR = x;
|
||||
}
|
||||
|
||||
// delay procedures
|
||||
|
||||
inline static void delayu() {
|
||||
asm volatile ("nop");
|
||||
}
|
||||
|
||||
static void delaym(uint8_t value) {
|
||||
for (uint8_t outer = 0; outer < value; outer++) {
|
||||
for (uint16_t inner = 0; inner < DELAY_M; inner++) {
|
||||
delayu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SPI communication
|
||||
|
||||
#define SPI_DDR DDRB
|
||||
#define SPI_PORT PORTB
|
||||
#define SPI_PIN PINB
|
||||
#define SPI_SCLK 5 // Arduino Pin 13 <-> Bit 5 of port B
|
||||
#define SPI_MISO 4 // Arduino Pin 12 <-> Bit 4 of port B
|
||||
#define SPI_MOSI 3 // Arduino Pin 11 <-> Bit 3 of port B
|
||||
#define SPI_CSN 2 // Arduino Pin 10 <-> Bit 2 of port B
|
||||
#define SPI_CE 1 // Arduino Pin 9 <-> Bit 1 of port B
|
||||
#define csnlow() SPI_PORT &= ~_BV(SPI_CSN)//;delaym(1) // not necessary
|
||||
#define csnhigh() SPI_PORT |= _BV(SPI_CSN)//;delaym(1) // not necessary
|
||||
#define celow() SPI_PORT &= ~_BV(SPI_CE)//;delaym(1) // not necessary
|
||||
#define cehigh() SPI_PORT |= _BV(SPI_CE)//;delaym(1) // minimum puls width 10us
|
||||
|
||||
static void SPIinit() {
|
||||
// set pin mode: MOSI,SCLK,CE = OUTPUT, MISO = INPUT
|
||||
SPI_DDR = _BV(SPI_MOSI) | _BV(SPI_SCLK) | _BV(SPI_CE) | _BV(SPI_CSN) | ~_BV(SPI_MISO);
|
||||
}
|
||||
|
||||
static uint8_t SPItransfer(uint8_t value) {
|
||||
for(uint8_t i = 0 ;i < 8;i++) {
|
||||
if (value & 0x80) SPI_PORT |= _BV(SPI_MOSI); else SPI_PORT &= ~_BV(SPI_MOSI);
|
||||
delayu();
|
||||
value <<= 1;
|
||||
SPI_PORT |= _BV(SPI_SCLK);
|
||||
delayu();
|
||||
value |= ((SPI_PIN >> SPI_MISO) & 0x01);
|
||||
SPI_PORT &= ~_BV(SPI_SCLK);
|
||||
delayu();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// UART debug
|
||||
|
||||
static void uart_init() {
|
||||
//Double speed mode USART0
|
||||
UART_SRA = _BV(U2X0);
|
||||
UART_SRB = _BV(RXEN0) | _BV(TXEN0);
|
||||
UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
|
||||
UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
|
||||
}
|
||||
|
||||
void putch(char ch) {
|
||||
while (!(UART_SRA & _BV(UDRE0)));
|
||||
UART_UDR = ch;
|
||||
}
|
||||
|
||||
uint8_t getch() {
|
||||
uint8_t ch;
|
||||
// wait until char received
|
||||
while(!(UART_SRA & _BV(RXC0)));
|
||||
// framing error?
|
||||
if (!(UART_SRA & _BV(FE0))) {
|
||||
watchdogReset();
|
||||
}
|
||||
ch = UART_UDR;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static void put_string(char *s) {
|
||||
while (*s)
|
||||
putch(*s++);
|
||||
}
|
||||
|
||||
static void put_int(uint8_t i) {
|
||||
char s[5];
|
||||
itoa( i, s, 10 );
|
||||
put_string( s );
|
||||
}
|
||||
|
||||
|
||||
#endif // MYSBootloaderHW_H
|
||||
@@ -0,0 +1,210 @@
|
||||
#ifndef MYSBootloaderRF24_H
|
||||
#define MYSBootloaderRF24_H
|
||||
|
||||
#include "MYSBootloaderHW.h"
|
||||
#include "nRF24L01.h"
|
||||
|
||||
typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ;
|
||||
typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e;
|
||||
|
||||
#define _write_register(reg) ( (reg) | W_REGISTER)
|
||||
#define _read_register(reg) ( (reg) | R_REGISTER)
|
||||
|
||||
#define addr_width 5
|
||||
uint64_t pipe0_reading_address = 0;
|
||||
|
||||
|
||||
uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) {
|
||||
csnlow();
|
||||
uint8_t status = SPItransfer( addr );
|
||||
while ( len-- ) {
|
||||
status = SPItransfer(NOP);
|
||||
*buf++ = status;
|
||||
}
|
||||
csnhigh();
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) {
|
||||
csnlow();
|
||||
uint8_t status = SPItransfer( addr );
|
||||
while ( len-- ) {
|
||||
status = SPItransfer(*buf++);
|
||||
}
|
||||
csnhigh();
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t ReadAddress(uint8_t addr) {
|
||||
return BurstReadAddress(addr,NULL,1);
|
||||
}
|
||||
|
||||
|
||||
uint8_t WriteAddress(uint8_t addr, uint8_t val) {
|
||||
return BurstWriteAddress(addr,&val,1);
|
||||
}
|
||||
|
||||
#define BurstWriteRegister(reg,buf,len) BurstWriteAddress(_write_register(reg),buf,len)
|
||||
#define WriteRegister(reg,val) WriteAddress(_write_register(reg),val)
|
||||
#define BurstReadRegister(reg,buf,len) BurstReadAddress(_read_register(reg),buf,len)
|
||||
#define ReadRegister(reg) ReadAddress(_read_register(reg))
|
||||
#define get_status() BurstReadAddress(NOP,NULL,0)
|
||||
|
||||
void Flush_RX(void) {
|
||||
BurstWriteAddress(FLUSH_RX,NULL,0);
|
||||
}
|
||||
|
||||
void Flush_TX(void) {
|
||||
BurstWriteAddress(FLUSH_TX,NULL,0);
|
||||
}
|
||||
|
||||
void Flush_RXTX_CLI(void) {
|
||||
// flush RX and TX buffer
|
||||
Flush_RX();
|
||||
Flush_TX();
|
||||
// clear registers
|
||||
WriteRegister(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
|
||||
}
|
||||
|
||||
static void startListening(void) {
|
||||
// for size reasons, we do not read back the register content, set PRIM_RX
|
||||
WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | _BV(PRIM_RX)) ;
|
||||
Flush_RXTX_CLI();
|
||||
// write pipe0 address
|
||||
if (pipe0_reading_address){
|
||||
BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&pipe0_reading_address, addr_width);
|
||||
}
|
||||
cehigh();
|
||||
}
|
||||
|
||||
static void stopListening(void) {
|
||||
celow();
|
||||
Flush_RXTX_CLI();
|
||||
// for size reasons, we do not read back the register content, clear PRIM_RX
|
||||
WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX)) ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool write_buf(uint8_t* buf, uint8_t len, const bool multicast ) {
|
||||
// write payload to FIFO
|
||||
BurstWriteRegister( multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, buf, len) ;
|
||||
// CE pulse to start transmission
|
||||
cehigh();
|
||||
// IMPORTANT: minimum CE pulse width 10us, see nRF24L01 specs
|
||||
delaym(1);
|
||||
// start transmitting
|
||||
celow();
|
||||
|
||||
|
||||
// wait until sent, here we potentially have a deadlock when transmitter not connected/not working
|
||||
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
|
||||
// read interrupts and clear
|
||||
uint8_t status = WriteRegister(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
|
||||
// max retries? clear FIFO
|
||||
if( status & _BV(MAX_RT)){
|
||||
Flush_TX();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t getDynamicPayloadSize(void) {
|
||||
uint8_t result = ReadAddress(R_RX_PL_WID);
|
||||
if(result > 32) {
|
||||
Flush_RX();
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static bool available(uint8_t* pipe_num) {
|
||||
if (!(ReadAddress(FIFO_STATUS) & _BV(RX_EMPTY) )){
|
||||
if (pipe_num) {
|
||||
uint8_t status = get_status();
|
||||
*pipe_num = ( status >> RX_P_NO ) & 0b111;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void readMessage(uint8_t* buf) {
|
||||
// read payload
|
||||
BurstReadRegister(R_RX_PAYLOAD,buf,getDynamicPayloadSize());
|
||||
// reset interrupts
|
||||
WriteRegister(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS));
|
||||
}
|
||||
|
||||
static void openWritingPipe(uint64_t value) {
|
||||
BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&value, addr_width);
|
||||
BurstWriteRegister(TX_ADDR, (uint8_t*)&value, addr_width);
|
||||
}
|
||||
|
||||
static void openReadingPipe(uint8_t pipe, uint64_t address) {
|
||||
// If this is pipe 0, cache the address. This is needed because openWritingPipe() will overwrite the pipe 0 address, so
|
||||
// startListening() will have to restore it
|
||||
if (pipe == WRITE_PIPE) {
|
||||
memcpy(&pipe0_reading_address,&address,addr_width);
|
||||
}
|
||||
// only write full address for pipe 0 and 1
|
||||
// since we do not use the BROADCASTING PIPE in this bootloader, we can remove the check
|
||||
//if ( pipe < 2 )
|
||||
{
|
||||
BurstWriteRegister(RX_ADDR_P0 + pipe, (uint8_t*)&address, addr_width);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static boolean write(uint8_t destination, uint8_t* buf, uint8_t len, boolean multicast) {
|
||||
stopListening();
|
||||
openWritingPipe(TO_ADDR(destination));
|
||||
bool result = write_buf(buf, len, multicast);
|
||||
startListening();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void setAddress(uint8_t addr)
|
||||
{
|
||||
if (addr != BROADCAST_ADDRESS) {
|
||||
openReadingPipe(WRITE_PIPE, TO_ADDR(addr));
|
||||
openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(addr));
|
||||
// enable pipe
|
||||
WriteRegister(EN_RXADDR,_BV(ERX_P0 + WRITE_PIPE) | _BV(ERX_P0 + CURRENT_NODE_PIPE));
|
||||
}
|
||||
}
|
||||
|
||||
static void RFinit(void){
|
||||
// set address width
|
||||
WriteRegister(SETUP_AW, (addr_width-2) % 4);
|
||||
// auto retransmit delay 1500us, auto retransmit count 15
|
||||
WriteRegister(SETUP_RETR, 5 << ARD | 15 << ARC);
|
||||
// enable auto ack on all pipes, except broadcasting pipe
|
||||
WriteRegister(EN_AA, 0b00111111 & ~_BV(BROADCAST_PIPE)) ;
|
||||
// set channel
|
||||
WriteRegister(RF_CH, RF24_CHANNEL);
|
||||
// set data rate
|
||||
WriteRegister(RF_SETUP, ((RF24_DATARATE & 0b00000010 ) << 4) | ((RF24_DATARATE & 0b00000001 ) << 3) | ((RF24_PA_LEVEL << 1) + 1));
|
||||
// flush RX and TX FIFO, clear interrupts
|
||||
Flush_RXTX_CLI();
|
||||
// activate dynamic payload feature
|
||||
WriteAddress(ACTIVATE,0x73);
|
||||
// enable payload with ACK and dynamic payload length
|
||||
WriteRegister(FEATURE, _BV(EN_ACK_PAY) | _BV(EN_DPL) );
|
||||
// Enable dynamic payload length on all pipes
|
||||
WriteRegister(DYNPD, _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0));
|
||||
//powerUp, enable 16bit CRC, no RX mode
|
||||
WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX));
|
||||
// stabilize
|
||||
delaym(5);
|
||||
|
||||
// We are not listening to broadcast pipe since we do not reply to broadcasting messages (e.g. FIND_PARENT_RESPONSE messages)
|
||||
// openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // MYSBootloaderRF24_H
|
||||
@@ -0,0 +1,65 @@
|
||||
PROJECT = MYSBootloader
|
||||
INCLUDES = ../libraries/MySensors
|
||||
|
||||
MCU = atmega328p
|
||||
CLK = 16000000L
|
||||
|
||||
ISP_PORT = com5
|
||||
ISP_SPEED = 115200
|
||||
ISP_PROTOCOL = stk500v2
|
||||
ISP_MCU = m328p
|
||||
ISP_HFUSE = DA
|
||||
ISP_LFUSE = F7
|
||||
ISP_EFUSE = 06
|
||||
ISP_ARGS = -c$(ISP_PROTOCOL) -P$(ISP_PORT) -b$(ISP_SPEED) -p$(ISP_MCU)
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
BINPATH = C:/Program Files (x86)/Arduino/hardware/tools/avr/bin/
|
||||
JAVAPATH = C:/Program Files/Java/jdk1.7.0_40/bin/
|
||||
JAVAC = $(JAVAPATH)javac.exe
|
||||
JAVA = $(JAVAPATH)java.exe
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_P := $(shell uname -p)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(UNAME_P),x86_64)
|
||||
BINPATH = ../arduino/bin-linux64/bin/
|
||||
else ifneq ($(filter %86,$(UNAME_P)),)
|
||||
BINPATH = ../arduino/bin-linux32/bin/
|
||||
else ifneq ($(filter arm%,$(UNAME_M)),)
|
||||
BINPATH = /usr/bin/
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS = -x c -mno-interrupts -funsigned-char -funsigned-bitfields -DF_CPU=$(CLK)-Os -fno-inline-small-functions -fno-split-wide-types -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -mrelax -Wall -mmcu=$(MCU) -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"
|
||||
LDFLAGS = -nostartfiles -Wl,-s -Wl,-static -Wl,-Map="$(OutputFileName).map" -Wl,--start-group -Wl,--end-group -Wl,--gc-sections -mrelax -Wl,-section-start=.text=0x7800 -mmcu=$(MCU)
|
||||
|
||||
|
||||
|
||||
all: clean out
|
||||
|
||||
clean:
|
||||
- rm *.o
|
||||
- rm *.elf
|
||||
- rm *.hex
|
||||
|
||||
$(PROJECT).o: $(PROJECT).c
|
||||
"$(BINPATH)avr-gcc" $(CFLAGS) -I$(INCLUDES) $< -o $@
|
||||
|
||||
$(PROJECT).elf: $(PROJECT).o
|
||||
"$(BINPATH)avr-gcc" $(LDFLAGS) -o $@ $< -lm
|
||||
|
||||
$(PROJECT).hex: $(PROJECT).elf
|
||||
"$(BINPATH)avr-objcopy" -O ihex -R .eeprom $< $@
|
||||
|
||||
out: $(PROJECT).hex
|
||||
"$(BINPATH)avr-size" $(PROJECT).elf
|
||||
|
||||
load: clean out isp
|
||||
|
||||
isp: $(PROJECT).hex
|
||||
"$(BINPATH)avrdude" $(ISP_ARGS) -e -u -Ulock:w:0x3f:m -qq -Uefuse:w:0x$(ISP_EFUSE):m -Uhfuse:w:0x$(ISP_HFUSE):m -Ulfuse:w:0x$(ISP_LFUSE):m -Ulock:w:0x0f:m
|
||||
"$(BINPATH)avrdude" $(ISP_ARGS) -V -q -s -Uflash:w:$(PROJECT).hex
|
||||
"$(BINPATH)avrdude" $(ISP_ARGS)
|
||||
@@ -0,0 +1,62 @@
|
||||
# See: http://code.google.com/p/arduino/wiki/Platforms
|
||||
|
||||
menu.cpu=Processor
|
||||
|
||||
uno.name=Arduino Uno Optiboot
|
||||
|
||||
uno.vid.0=0x2341
|
||||
uno.pid.0=0x0043
|
||||
uno.vid.1=0x2341
|
||||
uno.pid.1=0x0001
|
||||
|
||||
uno.upload.tool=avrdude
|
||||
uno.upload.protocol=arduino
|
||||
uno.upload.maximum_size=32256
|
||||
uno.upload.maximum_data_size=2048
|
||||
uno.upload.speed=115200
|
||||
|
||||
uno.bootloader.tool=avrdude
|
||||
uno.bootloader.low_fuses=0xFF
|
||||
uno.bootloader.high_fuses=0xDE
|
||||
uno.bootloader.extended_fuses=0x05
|
||||
uno.bootloader.unlock_bits=0x3F
|
||||
uno.bootloader.lock_bits=0x0F
|
||||
uno.bootloader.file=optiboot/optiboot_atmega328.hex
|
||||
|
||||
uno.build.mcu=atmega328p
|
||||
uno.build.f_cpu=16000000L
|
||||
uno.build.board=AVR_UNO
|
||||
uno.build.core=arduino
|
||||
uno.build.variant=standard
|
||||
|
||||
|
||||
## Arduino Pro or Pro Mini (3V3 & 5V, 16 MHz) w/ ATmega328 MYSBootloader
|
||||
## -------------------------------------------------
|
||||
|
||||
|
||||
proMYSBL.name=ATmega328 16Mhz MYSBootloader
|
||||
|
||||
proMYSBL.upload.tool=avrdude
|
||||
proMYSBL.upload.protocol=arduino
|
||||
proMYSBL.upload.maximum_size=30720
|
||||
proMYSBL.upload.maximum_data_size=2048
|
||||
proMYSBL.upload.speed=115200
|
||||
|
||||
proMYSBL.bootloader.tool=avrdude
|
||||
proMYSBL.bootloader.low_fuses=0xF7
|
||||
proMYSBL.bootloader.high_fuses=0xDA
|
||||
proMYSBL.bootloader.extended_fuses=0x06
|
||||
proMYSBL.bootloader.unlock_bits=0x3F
|
||||
proMYSBL.bootloader.lock_bits=0x0F
|
||||
proMYSBL.bootloader.file=MySensors/MYSBootloader.hex
|
||||
|
||||
proMYSBL.build.mcu=atmega328p
|
||||
proMYSBL.build.f_cpu=16000000L
|
||||
proMYSBL.build.board=AVR_UNO
|
||||
proMYSBL.build.core=arduino
|
||||
proMYSBL.build.variant=standard
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user