Hey all,
I have an ATmega328PB that is running a (modified) version of Optiboot that come packaged with the MiniCore project for the Arduino IDE.
I have a second MCU (a STM32) that is conencted to my ATmega's UART lines. There is a peripheral subcircuit that resets the ATmega using its reset pin if the UART_RX line of the ATmega gets pulled low for more than 1 second.
I would like to use the STM32 to program my ATmega via the Optiboot bootloader using only these RX and TX lines.
I have the following code running on the STM32, however it doesn't seem to work. I think the STK500 protocol wants me to send a CRC byte after the STK_READ_SIGN command byte... or something....
/** * * This file contains an application that runs on the Electron which attempts to reset an ATmega328PB with an optiboot bootloader pre-loaded onto it, and then immediately query the ATmega328PB for its signature bytes to confirm that the bootloader is functioning correctly. * */ #include "Particle.h" /*=============================================>>>>> = Optiboot-implemented STK-500 command codes = ===============================================>>>>>*/ #define STK_READ_SIGN 0x75 /*= End of Optiboot-implemented STK-500 command codes =*/ /*=============================================<<<<<*/ #define OPTIBOOT_BAUD_RATE 115200 enum serial_test_cmd_codes_t{ CMD_READ_SIGNATURE_BYTES = 1, CMD_RESET_TARGET_MCU = 2 }; //Configure how the app runs within the Particle ecosystem SYSTEM_MODE(SEMI_AUTOMATIC); SYSTEM_THREAD(ENABLED); //Configure Particle system (runs this code before any Particle init code runs): STARTUP( //Configure other Particle runtime parameters System.enableFeature(FEATURE_RETAINED_MEMORY); //Enable system reset information System.enableFeature(FEATURE_RESET_INFO); //Make sure that cloud DFU updates are enabled System.enableUpdates(); ); //Define log handler using serial monitor (dumps logs to serial monitor) SerialLogHandler logHandler( LOG_LEVEL_ALL, //Particle OS debug level { {"app" , LOG_LEVEL_ALL }, //All log names with "app" prefix } ); const char* logName = "app.main"; static Logger myLog(logName); //Logger object used in this "main.cpp" file void setup(){ //Start UART peripheral communicating with attached PC via Electron USB port Serial.begin(57600); //Start UART peripheral communicating with attached ATmega Serial1.begin(115200); } void loop(){ if(Serial.available()){ byte cmd_byte = (byte)(Serial.read()); //Convert ascii byte into number cmd_byte -= 48; myLog.info("Received cmd_byte = %u", cmd_byte); switch(cmd_byte){ case CMD_READ_SIGNATURE_BYTES: { myLog.info("Resetting ATmega"); //Need to first reset target MCU using UART TX line by holding //it low for at least 0.8 seconds Serial1.end(); pinMode(TX, OUTPUT); digitalWrite(TX, LOW); delay(1500); //Debug myLog.info("Done - allowing ATmega to reset itself"); myLog.info("About to boot up ATmega and send STK_READ_SIGN (0x%0X)", STK_READ_SIGN); Serial.flush(); //Start the UART peripheral connected to the ATmega (this should) //in turn release the ATmega's reset pin Serial1.begin(OPTIBOOT_BAUD_RATE); //Wait a few moments for target MCU optiboot to spool up delay(1); //Request our signature bytes from the target MCU Serial1.write(STK_READ_SIGN); Serial1.flush(); //Now wait up to a second for ATmega optiboot to report signature bytes back unsigned int timerStart = millis(); // byte sig_bytes[3] = {0}; // byte bytesRead = 0; bool receivedResponse = false; while((millis() - timerStart) < 1000){ if(Serial1.available()){ myLog.info("Received byte: 0X%0X", Serial1.read()); timerStart = millis(); receivedResponse = true; } else if (!receivedResponse){ Serial1.write(STK_READ_SIGN); delay(10); } }; if((millis() - timerStart) > 1000){ myLog.error("Didn't received signature bytes from ATmega"); } } break; default: { myLog.error("Invalid PC command.... going into DFU mode..."); Serial.flush(); System.dfu(); } } } }
Can anyone point me to an example of a program that implements the STK500 protocol on a microcontroller in order to flash a secondary microcontroller?