The recent project requires the development of an STM8 upgrade system, that is, upgrading the APP through IAP
MCU: STM8S207M8 (different models only need to modify the corresponding interface)
IAP:bootloader v1.1(AN2659code)
APP: Project Application
Development environment: STVD+Cosmic
Upgrade software: Official Flash Loader Demonstrator (customized according to the protocol later)
Principle: After the MCU is powered on, it starts to execute from address 0x8000, which is the starting code of the bootloader. When the synchronization code is received, it enters the upgrade state.
Waiting for the command, the upgrade software is flash-programmed through the protocol. After completion, jump to the starting address of the code APP, and the MCU starts to execute the APP program.
In the APP state, if a firmware upgrade command is received, it jumps to the starting address 0x8000 where the IAP is located and enters the upgrade state.
PS: STM8 has a bootloader built in, which can be opened through the configuration word OPTBL. This bootloader supports iap uart download program.
Due to the hardware situation, there is no need for a built-in bootloader here. The bootlader mentioned here refers to IAP.
1. Since the bootloader code is not based on STM8S207M8, it needs to be transplanted. The main problem is the segment definition problem.
According to the usage of the code segments, define the corresponding segments in setting->Linker->Input, especially the FLASH_CODE segment.
Because this segment is defined in RAM, it means that all FLASH operations are performed in RAM.
Note that the interrupt vector address is set to 0x8000 and the starting address of the code segment is 0x8080 (the vector table occupies 0x80 bytes).
Pay attention to setting C compilation optimization, because if the code is too large, the starting address of the APP should be moved backwards to avoid overlap.
In addition, some configurations should be done in the main.h file according to the size of FLASH, EEPROM, RAM, etc.:
#define BLOCK_BYTES 128
#define ADDRESS_BLOCK_MASK (BLOCK_BYTES - 1)
//memory boundaries
#define RAM_START 0x000000ul
#define RAM_END 0x0017FFul
#define EEPROM_START 0x004000ul
#define EEPROM_END 0x0047FFul
#define OPTION_START 0x004800ul
#define OPTION_END 0x00487Ful
#define UBC_OPTION_LOCATION 0x004801
#define FLASH_START 0x008000ul
#define FLASH_END 0x017FFFul
#define BLOCK_SIZE 0x80
#define BLOCK_PER_SECTOR 0x08
#define BLOCK_BYTES 128
#define ADDRESS_BLOCK_MASK (BLOCK_BYTES - 1)
//memory boundaries
#define RAM_START 0x000000ul
#define RAM_END 0x0017FFul
#define EEPROM_START 0x004000ul
#define EEPROM_END 0x0047FFul
#define OPTION_START 0x004800ul
#define OPTION_END 0x00487Ful
#define UBC_OPTION_LOCATION 0x004801
#define FLASH_START 0x008000ul
#define FLASH_END 0x017FFFul
#define BLOCK_SIZE 0x80
#define BLOCK_PER_SECTOR 0x08
2. The compiled IAP is downloaded to the flash starting address 0x8000 through the SWIM tool.
3. According to the communication protocol "STM8 bootloader.pdf", adjust the serial communication operation between the upgrade software and IAP.
4. Here the starting address of the custom APP is 0xa000, so in the APP project, set the interrupt vector address to 0xa000 and the starting address of the code segment to 0xa080.
5. Jump to IAP in APP and use assembly _asm("jp $0x8000").
APP interrupt execution process:
Since the interrupt vector table of STM8 is fixed, when STM8 generates an interrupt exception, it will jump to 0x8000 and execute the corresponding interrupt service program. However, the starting address of the interrupt vector of APP is 0xa000.
Then to execute the real interrupt service program, it is necessary to redirect the interrupt vector of IAP. The general processing is to jump from the vector table of IAP to the interrupt vector table of APP.
Let's first look at the IAP interrupt vector table:
extern void _stext(); /* startup routine */
struct interrupt_vector const UserISR_IRQ[32] @ MAIN_USER_RESET_ADDR;
//redirected interrupt table
struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 2)}, /* irq0 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 3)}, /* irq1 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 4)}, /* irq2 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 5)}, /* irq3 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 6)}, /* irq4 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 7)}, /* irq5 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 8)}, /* irq6 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 9)}, /* irq7 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+10)}, /* irq8 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+11)}, /* irq9 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+12)}, /* irq10 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+13)}, /* irq11 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+14)}, /* irq12 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+15)}, /* irq13 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+16)}, /* irq14 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+17)}, /* irq15 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+18)}, /* irq16 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+19)}, /* irq17 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+20)}, /* irq18 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+21)}, /* irq19 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+22)}, /* irq20 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+23)}, /* irq21 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+24)}, /* irq22 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+25)}, /* irq23 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+26)}, /* irq24 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+27)}, /* irq25 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+28)}, /* irq26 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+29)}, /* irq27 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+30)}, /* irq28 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+31)}, /* irq29 */
};
extern void _stext(); /* startup routine */
struct interrupt_vector const UserISR_IRQ[32] @ MAIN_USER_RESET_ADDR;
//redirected interrupt table
struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 2)}, /* irq0 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 3)}, /* irq1 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 4)}, /* irq2 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 5)}, /* irq3 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 6)}, /* irq4 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 7)}, /* irq5 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 8)}, /* irq6 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 9)}, /* irq7 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+10)}, /* irq8 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+11)}, /* irq9 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+12)}, /* irq10 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+13)}, /* irq11 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+14)}, /* irq12 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+15)}, /* irq13 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+16)}, /* irq14 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+17)}, /* irq15 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+18)}, /* irq16 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+19)}, /* irq17 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+20)}, /* irq18 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+21)}, /* irq19 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+22)}, /* irq20 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+23)}, /* irq21 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+24)}, /* irq22 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+25)}, /* irq23 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+26)}, /* irq24 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+27)}, /* irq25 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+28)}, /* irq26 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+29)}, /* irq27 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+30)}, /* irq28 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+31)}, /* irq29 */
};
Note the definition of MAIN_USER_RESET_ADDR:
#define MAIN_USER_RESET_ADDR 0xA000ul
#define MAIN_USER_RESET_ADDR 0xA000ul
MAIN_USER_RESET_ADDR is the starting address of the APP and also the address of the interrupt vector table of the APP.
From the interrupt vector table of IAP, we can see that when APP generates an interrupt, it will first jump to the interrupt vector table of IAP, and then jump from the interrupt vector table of IAP to the interrupt vector table of APP.
About the clock configuration:
When the MCU is started by the internal clock, it often needs to switch to the external high-speed clock HSE after startup. The general code operation is:
CLK_DeInit();
CLK_HSECmd(ENABLE);
while(SET != CLK_GetFlagStatus(CLK_FLAG_HSERDY));
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
CLK_ClockSwitchCmd(ENABLE);
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO , CLK_SOURCE_HSE , DISABLE , CLK_CURRENTCLOCKSTATE_ENABLE);
CLK_DeInit();
CLK_HSECmd(ENABLE);
while(SET != CLK_GetFlagStatus(CLK_FLAG_HSERDY));
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
CLK_ClockSwitchCmd(ENABLE);
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO , CLK_SOURCE_HSE , DISABLE , CLK_CURRENTCLOCKSTATE_ENABLE);
There is no problem here, but it should be noted that if both IAP and APP switch the clock to HSE, the code may always be in this loop:
while(SET != CLK_GetFlagStatus(CLK_FLAG_HSERDY));
while(SET != CLK_GetFlagStatus(CLK_FLAG_HSERDY));
So after switching the clock in the bootloader, do not repeat the operation in the APP.
About the IAP BUG of this version:
void ProcessCommands(void)
{
u8 result;
do
{
//init pointer
ReceivedData = DataBuffer;
//receive data 1-st byte
if(!Receive(ReceivedData++))
continue;
//receive data 2-nd byte
if(!Receive(ReceivedData++))
continue;
//check if command bytes are complements
if(DataBuffer[N_COMMAND] != (u8)(~DataBuffer[N_NEG_COMMAND]))
{
Transmit(NACK);
continue;
}
//parse commands
Transmit(ACK);
switch(DataBuffer[0])
{
case (GT_Command): result = GT_Command_Process(); break;
case (RM_Command): result = RM_Command_Process(); break;
case (GO_Command): result = GO_Command_Process(); break;
case (WM_Command): result = WM_Command_Process(); break;
case (EM_Command): result = EM_Command_Process(); break;
}
}while( (!result) || (DataBuffer[0] != GO_Command) ); //until GO command received
void ProcessCommands(void)
{
u8 result;
do
{
//init pointer
ReceivedData = DataBuffer;
//receive data 1-st byte
if(!Receive(ReceivedData++))
continue;
//receive data 2-nd byte
if(!Receive(ReceivedData++))
continue;
//check if command bytes are complements
if(DataBuffer[N_COMMAND] != (u8)(~DataBuffer[N_NEG_COMMAND]))
{
Transmit(NACK);
continue;
}
//parse commands
Transmit(ACK);
switch(DataBuffer[0])
{
case (GT_Command): result = GT_Command_Process(); break;
case (RM_Command): result = RM_Command_Process(); break;
case (GO_Command): result = GO_Command_Process(); break;
case (WM_Command): result = WM_Command_Process(); break;
case (EM_Command): result = EM_Command_Process(); break;
}
}while( (!result) || (DataBuffer[0] != GO_Command) ); //until GO command received
Among them, DataBuffer is a global array:
//input buffer
u8 DataBuffer[130];
//input buffer
u8 DataBuffer[130];
From the ProcessCommands function, we can see that the DataBuffer[0] value is judged in the switch, and DataBuffer[0] != GO_Command is still used in the loop to judge.
Since the communication buffer is DataBuffer during the execution of XX_Command_Process, the value of DataBuffer[0] may change.
When the value is changed to GO_Command, the loop may be jumped out, causing the function ProcessCommands to return and fail to execute the next command, thus causing an error.
The author encountered this problem. During the upgrade process, 181 pages of data needed to be written. The first data received in the 61st page of data was 0x21, which was the value of GO_Command.
This makes it impossible to continue upgrading. So you need to change it to:
u8 cmd;
... ...
cmd = DataBuffer[0];
switch(cmd)
{
case (GT_Command): result = GT_Command_Process(); break;
case (RM_Command): result = RM_Command_Process(); break;
case (GO_Command): result = GO_Command_Process(); break;
case (WM_Command): result = WM_Command_Process(); break;
case (EM_Command): result = EM_Command_Process(); break;
}
hile( (!result) || (cmd != GO_Command) ); //until GO command received
Previous article:STM8 block operations
Next article:STM8 UART2
Recommended ReadingLatest update time:2024-11-17 00:35
- Popular Resources
- Popular amplifiers
- STM8 C language programming (1) - basic program and startup code analysis
- Description of the BLDC back-EMF sampling method based on the STM8 official library
- STM32 MCU project example: Smart watch design based on TouchGFX (8) Associating the underlying driver with the UI
- uCOS-III Kernel Implementation and Application Development Practical Guide - Based on STM32 (Wildfire)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- PCB carries 10A current
- EEWORLD University ---- VICOR High Performance Power Conversion Seminar (2019.11)
- Simplify Your BOM with High Voltage Amplifiers
- TI Bluetooth Low Energy/Zigbee RF4CE Remote Control
- The wireless receiving signal brightness DBM is negative
- Internal DDR routing training materials
- Million-dollar annual salary recruitment: Data algorithm engineer (video big data)
- TMS320F28377D_LED Experiment
- IPC-7711/7721B CN IPC-A-630 Chinese version
- MicroPython upgraded to v1.15