
Introduction
Based on the overall plan: Renesas A4T1 standard OTA solution / reset functionBy adding file transfer functionality to the core project, all OTA tasks can be completed.This article is not difficult, but there is a lot of content and some pitfalls; it is a small comprehensive guide.
File Transfer Host


Key Points for C# Host
First, addControlCAN.dll, then you can directly call the functions in the dll with pure code.
This way, you can develop the CAN box.
The host code does not need to change!

Experimental Environment of This Article

No changes: JLINK + CAN box + 5V power supply
Lower Machine Work Steps
First, start from the core projectThen add flash operation code to both BOOT and APPThenAdd file transfer code to both BOOT and APPAnd it’s done!The APP projectrecords the filesize to dataflash after receiving the file information frame and then resets, that’s all.The BOOT projectinteracts with the host to handle file request frames/file content frames, finally records the bin file to code flash and clears dataflash, then resets.
It must be noted
I did a simple test
Found that dataflash is really difficult to operate.
It’s inexplicable and confusing.
I plan to spend time researching it later.
This article places the filesize marker in the last 8K of code flash.
#define APPLICATION_CONFIG_ADDRESS FLASH_HP_CF_BLOCK_7
/* 8 KB: 0x0000E000 – 0x0000FFFF */

Record of Pitfalls


Environmental Issues
It seems I encountered this problem in the IAR environment.
After I burned ALL.bin to A4T1
Then I operated to debug the APP project independently, everything was normal.
Then I operated to debug the BOOT project independently and found that it would erase all subsequent flash space to FFFFF, which is a troublesome point.
In the IAR environment, there are two buttons.
One is for programming and debugging, and the other is for just debugging; choose the latter.
I don’t know how to set it in E2STUDIO.
Fortunately, the functionality of this article is not complex, and I succeeded quickly.
In the future, placing the filesize marker in dataflash would be better.

Points to Note for Code Flash Operations
The minimum write granularity of code flash is 128 bytes.
The actual performance is that if the parameter of R_FLASH_HP_Write – write num bytes, if you pass 4 or 64, it will crash; it must be a multiple of 128, such as 128 or 1024.
So for the APP project, prepare a global 128-byte array uint8_t g_src_uint8[128] to maintain the minimum write, for example, copy to the array before writing U32.
So for the BOOT project, the last packet received from the host may not be exactly 1024; wewill write 1024 bytes forcefully in the last packet.code_flash_write(addr,data,OTA_ONE_STEP); to ensure the write functionR_FLASH_HP_Write does not crash (or the host packs app.bin in advance to fill to a multiple of 1024; of course, the lower machine should standardize it, which is this article).

Code flash operations are blocking!
Interrupts need to be disabled before and after!
No need for lower-level callbacks!
This article does not operate dataflash, sono lower-level callback functions are written.
Dataflash will be discussed later.

Hidden Details:
BOOT and APP are both anchored to a specific address 0X20001000.
So even though they are two projects,
the host RTT-VIEW can see the logs of both BOOT and APP.
No need to switch addresses.

Software Records

The merge script is a single line cp.bat
D:\OTA\merge\a.exe D:\OTA\BOOT\Debug\BOOT.bin D:\OTA\APP\Debug\APP.bin D:\OTA\merge\ALL.binpause
Below is the code for comparison and reading.

BOOT Code

// RX needs to rely heavily on the protocol to specify LEN to capture a complete data packet
#include "hal_data.h"
FSP_CPP_HEADER void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/************************************************LOG**************************************/
/************************************************LOG**************************************/
/************************************************LOG**************************************/
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "SEGGER_RTT.h"
#define LOG(...) SEGGER_RTT_printf(0 ,##__VA_ARGS__);
#define SHOWME LOG("[%s]\r\n",__FUNCTION__);
void LOG_ARRY(char *name,uint8_t *A,uint8_t len){ SEGGER_RTT_printf(0, "%s ",name); for (size_t i = 0; i < len; i++) { SEGGER_RTT_printf(0, "0X%02X ",A[i]); } SEGGER_RTT_printf(0, "\r\n");}
void LOG_INIT(void){ SEGGER_RTT_Init(); R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS); SEGGER_RTT_printf(0, "Hello MY89003 BOOT "); LOG("World\r\n");}
/************************************************LED**************************************/
/************************************************LED**************************************/
/************************************************LED**************************************/
void Led_Ctl(uint8_t flag){ if(flag){ R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_LOW); }else{ R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_HIGH); }}
void Led_Blink(void){ static char flag=0; Led_Ctl(flag); flag = !flag;}
/************************************************CAN**************************************/
/************************************************CAN**************************************/
/************************************************CAN**************************************/
#define APP_ERR_TRAP() __asm("BKPT #0\n") /* trap upon the error */fsp_err_t err = FSP_SUCCESS;/* Flags to be set in Callback function */bool b_canfd_tx_complete = false;bool b_canfd_rx_complete = false;bool b_canfd_err_status = false;/* CANFD RX and TX variables */can_frame_t g_can_tx_frame;can_frame_t g_can_rx_frame;
#define SAY_MY_NAME 0X60
/* Variable to store rx frame status info*/can_info_t can_rx_info ={ .error_code = 0, .error_count_receive = 0, .error_count_transmit = 0, .rx_fifo_status = 0, .rx_mb_status = 0, .status = 0,};
//* Set filter: only messages with ID 0X0060 will be accepted by my MCU, otherwise the lower-level CAN will discard it directly!const canfd_afl_entry_t p_canfd0_afl[CANFD_CFG_AFL_CH0_RULE_NUM] ={ { .id = { .id = SAY_MY_NAME, .frame_type = CAN_FRAME_TYPE_DATA, .id_mode = CAN_ID_MODE_STANDARD, }, .mask = { .mask_id = 0x7F0, .mask_frame_type = 0, .mask_id_mode = 1, }, .destination = { .minimum_dlc = CANFD_MINIMUM_DLC_0, .fifo_select_flags = CANFD_RX_FIFO_0, }, },};
void CAN_Init(void){SHOWME err = R_CANFD_Open(&g_canfd0_ctrl, &g_canfd0_cfg); if(FSP_SUCCESS != err) { APP_ERR_TRAP(); }}
// The PC is agreed to transmit a maximum of 1K of file each time#define OTA_ONE_STEP 1024
// Each packet of data has 8 bytes (T and L) in front of the data#define HEADLEN 8
// The final container for a frame of datauint8_t gloableCANRxBuf[OTA_ONE_STEP+HEADLEN];uint16_t gloableCANRxBufLen=0;
// How to send? Slow sending// MCU needs to send out a frame of data container considering that MCUTX is not very large, a container of 200 is sufficient; each time the lower-level CAN sends a maximum of 8 bytes#define CAN_TX_BUFFER_LENGTH 200uint8_t gloableCANTxBuf[CAN_TX_BUFFER_LENGTH];uint16_t gloableNumNeedTxCAN = 0;uint8_t gloableNumTxedCAN=0;
// How to receive? Use protocol to identify when a frame is completely received// Once the LEN is detected, process it; otherwise, do nothingvoid receive_timer_idle_isr(void){ handle_gloableCANRxBuf();}
void CANSend_Max_8Byte(uint8_t* data,uint8_t len){// Note for parameters: len<=8 memcpy((uint8_t*)&g_can_tx_frame.data[0], data, len); g_can_tx_frame.id = SAY_MY_NAME; g_can_tx_frame.id_mode = CAN_ID_MODE_STANDARD; g_can_tx_frame.type = CAN_FRAME_TYPE_DATA; g_can_tx_frame.data_length_code = len;//8; g_can_tx_frame.options = 0; err = R_CANFD_Write(&g_canfd0_ctrl, 0, &g_can_tx_frame); if(FSP_SUCCESS != err) { APP_ERR_TRAP(); }}
void send_task(void){ if(gloableNumNeedTxCAN==0) return;// No work to do if(b_canfd_tx_complete != true) return;// Not finished sending, refuse to execute, wait for the next cycle to execute
// Determine whether to send 8 or 4 uint8_t numAllLeft = gloableNumNeedTxCAN - gloableNumTxedCAN; uint8_t numNowSend = 0; if (numAllLeft >= 8) numNowSend = 8; else numNowSend = numAllLeft;
// Actually send out uint8_t arr[8] = {0}; for (int i = 0;i < numNowSend;i++) arr[i] = gloableCANTxBuf[i+ gloableNumTxedCAN]; CANSend_Max_8Byte(arr, numNowSend); b_canfd_tx_complete = false;
// Update the number of TX sent gloableNumTxedCAN += numNowSend;
LOG("gloableNumNeedTxCAN = %d gloableNumTxedCAN=%d numNowSend=%d\r\n",gloableNumNeedTxCAN,gloableNumTxedCAN,numNowSend); if(gloableNumTxedCAN == gloableNumNeedTxCAN) { gloableNumNeedTxCAN = 0; gloableNumTxedCAN = 0; }}
/******************* OTA Structured Function for RX Reception Service **********************/typedef struct _otaStartType{ uint32_t reboot; uint32_t filesize;}otaStartType;otaStartType otaStart;typedef struct _otaStartingType{ int addr; int len;}otaStartingType;otaStartingType otaStarting;bool sendOtaAsk(otaStartingType info){ // Process the Nth packet, publish request, pack data T is hardcoded to 0002 char body[100]={0}; char head[8]={0}; char bodylen=0;
bodylen=sprintf(body,"%s%d,%s%d}","{\"addr\":", \\ info.addr, \\ "\"len\":", \\ info.len);
LOG("bodylen=%d [%s]\r\n",bodylen,body);
char *type="0002"; sprintf(head,"%s%04X",type,bodylen);
LOG("head=[%s]\r\n",head);
// Asynchronously send memset(gloableCANTxBuf,0,CAN_TX_BUFFER_LENGTH); // Don't know why, the following assignment always shows an exception, body is tampered with!! Not studying for now //memcpy(&gloableCANTxBuf[0],head,8); //memcpy(&gloableCANTxBuf[8],body,len); // Still honestly repack TLV sprintf(gloableCANTxBuf,"%s%04X%s%d,%s%d}",type,bodylen,"{\"addr\":", \\ info.addr, \\ "\"len\":", \\ info.len);
LOG("gloableCANTxBuf=[%s]\r\n",gloableCANTxBuf);
// Trigger send task gloableNumNeedTxCAN = (bodylen+8); b_canfd_tx_complete = true; return true; }
char saveOtaData(uint8_t *data, uint16_t len);void ota_config_clear(void);
#define BHEX(A) ((A) <= '9'? (A) - '0' : ((A) >= 'A' && (A) <= 'F'? (A) + 10 - 'A' : (A) + 10 - 'a'))#define TYPE (BHEX(gloableCANRxBuf[0])<<12) | (BHEX(gloableCANRxBuf[1])<<8) | (BHEX(gloableCANRxBuf[2])<<4) | (BHEX(gloableCANRxBuf[3]))#define LEN (BHEX(gloableCANRxBuf[4])<<12) | (BHEX(gloableCANRxBuf[5])<<8) | (BHEX(gloableCANRxBuf[6])<<4) | (BHEX(gloableCANRxBuf[7]))#define VALUE &gloableCANRxBuf[8];
// Not using LENvoid handle_gloableCANRxBuf(void){ int needLen = gloableCANRxBufLen-HEADLEN; int trueLen = LEN; //LOG("needLen=%d trueLen=%d\r\n",needLen,trueLen);
if(trueLen != needLen) return;// Length mismatch
// Copy out and print char headStr[HEADLEN+1] = {0}; memcpy(headStr,gloableCANRxBuf,8); LOG("head [%s] \r\n",headStr);
uint8_t *body = VALUE; // MCU is an absolute slave; upon receiving a message, it must respond with a message switch (TYPE){ case 0x0001: LOG("\r\nNEVER IN BOOT WORLD\r\n"); break;
case 0x0003: saveOtaData(body,LEN);// Record 1024 bytes otaStarting.addr += otaStarting.len; int file_leave = otaStart.filesize - otaStarting.addr; LOG("\r\notaStart.filesize=%d otaStarting.addr=%d\r\n",otaStart.filesize,otaStarting.addr);
otaStarting.len = (file_leave > OTA_ONE_STEP)?OTA_ONE_STEP:file_leave; if(otaStarting.len){ sendOtaAsk(otaStarting); }else{ LOG("\r\nfile recve done\r\n"); ota_config_clear(); LOG("\r\nclear flag\r\n"); NVIC_SystemReset(); } break;
} gloableCANRxBufLen=0;}
/* Callback function from GUI interrupt service function; CAN received will run in here; CAN sent will also run in here*/void canfd0_callback(can_callback_args_t *p_args){ /* TODO: add your own code here */
//LOG("0X%04X \r\n",p_args->event); Led_Blink(); switch (p_args->event) { case CAN_EVENT_TX_COMPLETE: { b_canfd_tx_complete = true; LOG_ARRY("CAN_EVENT_TX_COMPLETE",g_can_tx_frame.data,g_can_tx_frame.data_length_code); break; } case CAN_EVENT_RX_COMPLETE: {
b_canfd_rx_complete = true; memcpy(&g_can_rx_frame, &p_args->frame, sizeof(can_frame_t)); LOG_ARRY("CAN_EVENT_RX_COMPLETE",g_can_rx_frame.data,g_can_rx_frame.data_length_code);
memcpy(&gloableCANRxBuf[gloableCANRxBufLen],g_can_rx_frame.data,g_can_rx_frame.data_length_code);
gloableCANRxBufLen+=g_can_rx_frame.data_length_code;
//LOG_ARRY("gloableCANRxBuf",gloableCANRxBuf,gloableCANRxBufLen); break; } case CAN_EVENT_ERR_WARNING: // Error warning event case CAN_EVENT_ERR_PASSIVE: // Error passive event case CAN_EVENT_ERR_BUS_OFF: // Error Bus Off event case CAN_EVENT_BUS_RECOVERY: // Bus recovery error event case CAN_EVENT_MAILBOX_MESSAGE_LOST: // Overwrite/overrun error event case CAN_EVENT_ERR_BUS_LOCK: // Bus lock detected (32 consecutive dominant bits). case CAN_EVENT_ERR_CHANNEL: // Channel error has occurred. case CAN_EVENT_TX_ABORTED: // Transmit abort event. case CAN_EVENT_ERR_GLOBAL: // Global error has occurred. case CAN_EVENT_FIFO_MESSAGE_LOST: // Transmit FIFO is empty. case CAN_EVENT_TX_FIFO_EMPTY: // Transmit FIFO is empty. { b_canfd_err_status = true; // Set flag bit break; } }}
/************************************************FLASH**************************************/
/************************************************FLASH**************************************/
/************************************************FLASH**************************************/
/* Code Flash */#define FLASH_HP_CF_BLOCK_SIZE_32KB (32*1024U) /* Block Size 32 KB */#define FLASH_HP_CF_BLOCK_SIZE_8KB (8*1024U) /* Block Size 8KB */
#define FLASH_HP_CF_BLOCK_0 (0x00000000U) /* 8 KB: 0x00000000 - 0x00001FFF */#define FLASH_HP_CF_BLOCK_1 (0x00002000U) /* 8 KB: 0x00002000 - 0x00003FFF */#define FLASH_HP_CF_BLOCK_2 (0x00004000U) /* 8 KB: 0x00004000 - 0x00005FFF */#define FLASH_HP_CF_BLOCK_3 (0x00006000U) /* 8 KB: 0x00006000 - 0x00007FFF */#define FLASH_HP_CF_BLOCK_4 (0x00008000U) /* 8 KB: 0x00008000 - 0x00009FFF */#define FLASH_HP_CF_BLOCK_5 (0x0000A000U) /* 8 KB: 0x0000A000 - 0x0000BFFF */#define FLASH_HP_CF_BLOCK_6 (0x0000C000U) /* 8 KB: 0x0000C000 - 0x0000DFFF */#define FLASH_HP_CF_BLOCK_7 (0x0000E000U) /* 8 KB: 0x0000E000 - 0x0000FFFF */#define FLASH_HP_CF_BLOCK_8 (0x00010000U) /* 32 KB: 0x00010000 - 0x00017FFF */#define FLASH_HP_CF_BLOCK_9 (0x00018000U) /* 32 KB: 0x00018000 - 0x0001FFFF */
/* Data Flash */#define FLASH_HP_DF_BLOCK_SIZE_64B (64U) /* Block Size 64 Byte */#define FLASH_HP_DF_BLOCK_0 (0x08000000U) /* Size 64 B: 0x08000000U - 0x0800003F */#define FLASH_HP_DF_BLOCK_1 (0x08000040U) /* Size 64 B: 0x08000040U - 0x0800007F */#define FLASH_HP_DF_BLOCK_2 (0x08000080U) /* Size 64 B: 0x08000080U - 0x080000BF */#define FLASH_HP_DF_BLOCK_3 (0x080000C0U) /* Size 64 B: 0x080000C0U - 0x080000FF */#define FLASH_HP_DF_BLOCK_63 (0x08000FC0U) /* Size 64 B: 0x08000FCOU - 0x08000FFF */#define FLASH_HP_DF_BLOCK_64 (0x08001000U) /* Size 64 B: 0x08001000U - 0x0800103F */
// Data flash requires callbacks, is a state machine, needs interrupts, needs callbacks // Code flash is blocking, do not need interrupts, do not need callbacks // This article does not use data flash, so no need to write callback functionsvoid my_flash_callback(flash_callback_args_t *p_args){}
void flash_init(void){ /* Open the flash lp instance. */ err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg); assert(FSP_SUCCESS == err);}
#define APPLICATION_CONFIG_ADDRESS FLASH_HP_CF_BLOCK_7 /* 8 KB: 0x0000E000 - 0x0000FFFF */
// This article uses the last 8K of code flash; erasing it will make it all 0XFFFFFFFFvoid x_flash_erase(void){ flash_result_t blank_check_result;
__disable_irq();
err = R_FLASH_HP_Erase(&g_flash0_ctrl, APPLICATION_CONFIG_ADDRESS, 1); assert(FSP_SUCCESS == err); err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, APPLICATION_CONFIG_ADDRESS, FLASH_HP_CF_BLOCK_SIZE_8KB, &blank_check_result); assert(FSP_SUCCESS == err); assert(FLASH_RESULT_BLANK == blank_check_result);
__enable_irq();}
// This article uses the last 8K of code flashvoid x_flash_read(void){ uint8_t four_u8[4]={0}; memcpy(four_u8, (uint8_t *) APPLICATION_CONFIG_ADDRESS, 4); LOG_ARRY("four_u8",four_u8,4); otaStart.filesize = four_u8[3]<<24|four_u8[2]<<16|four_u8[1]<<8|four_u8[0]; LOG("otaStart.filesize=%d",otaStart.filesize);}
// Read U32 and assign otaStart.filesizevoid ota_config_read(void){ x_flash_read();}
// Clear the marker for the BOOT-APP bondvoid ota_config_clear(void){ x_flash_erase();}
// Erase 64K of APP, no loop, just do 32K once and then 32K oncevoid code_flash_erase(void){ flash_result_t blank_check_result;
/* Disable interrupts to prevent vector table access while code flash is in P/E mode. */ __disable_irq();
err = R_FLASH_HP_Erase(&g_flash0_ctrl, FLASH_HP_CF_BLOCK_8, 1); assert(FSP_SUCCESS == err); err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, FLASH_HP_CF_BLOCK_8, FLASH_HP_CF_BLOCK_SIZE_32KB, &blank_check_result); assert(FSP_SUCCESS == err); assert(FLASH_RESULT_BLANK == blank_check_result);
err = R_FLASH_HP_Erase(&g_flash0_ctrl, FLASH_HP_CF_BLOCK_9, 1); assert(FSP_SUCCESS == err); err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, FLASH_HP_CF_BLOCK_9, FLASH_HP_CF_BLOCK_SIZE_32KB, &blank_check_result); assert(FSP_SUCCESS == err); assert(FLASH_RESULT_BLANK == blank_check_result);
/* Enable interrupts after code flash operations are complete. */ __enable_irq();}
// Address write 1Kvoid code_flash_write(uint32_t addr ,uint8_t *data, uint16_t len){
__disable_irq();
/* Write 32 bytes to the first block of data flash. */ // Write data, 4 8-bit data [but parameter 3 must be a multiple of 128, such as 128 or 1024 (128 is the MIN granularity)] err = R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t) data, addr, len); assert(FSP_SUCCESS == err);
__enable_irq();
// Close Flash controller // R_FLASH_HP_Close(&g_flash0_ctrl);}
/************************************************JUMP**************************************/
/************************************************JUMP**************************************/
/************************************************JUMP**************************************/
typedef void (*pFunction)(void);
#define APPLICATION_DATA_ADDRESS FLASH_HP_CF_BLOCK_8
void jump_application( void ){ uint32_t JumpAddress; pFunction Jump_To_Application;
/* Jump to user application */ JumpAddress = *(__IO uint32_t*) (APPLICATION_DATA_ADDRESS + 4); Jump_To_Application = (pFunction) JumpAddress;
__set_MSPLIM(0); SCB->VTOR = ((int)APPLICATION_DATA_ADDRESS & 0x1FFFFF80); __DSB();
/* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APPLICATION_DATA_ADDRESS); Jump_To_Application();}
char saveOtaData(uint8_t *data, uint16_t len){ LOG("saveOtaData len=%d\r\n",len); LOG_ARRY("binFile",data,len); uint32_t addr = (APPLICATION_DATA_ADDRESS + otaStarting.addr); code_flash_write(addr,data,OTA_ONE_STEP);// Note: here must write 1024 forcefully because A4T1 requires granularity; if not a multiple of 128, it will crash; here directly write 1024 return 0;}/*******************************************************************************************************************//** * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function * is called by main() when no RTOS is used. **********************************************************************************************************************/void hal_entry(void){ /* TODO: add your own code here */ LOG_INIT(); CAN_Init(); flash_init(); ota_config_read(); if(0XFFFFFFFF==otaStart.filesize){ jump_application(); return; }// If it enters IF, it goes directly to APP, ending the process// If it can go down, it enters the BOOT to receive firmware process
// 1> No backup firmware, collectively erase all 32K+32K code_flash_erase();// 2> Actively send the first file request otaStarting.addr=0; otaStarting.len=(OTA_ONE_STEP>otaStart.filesize)?(otaStart.filesize):(OTA_ONE_STEP); sendOtaAsk(otaStarting);// 3> Start TXRX process, loop to obtain firmware from the host while(1){ R_BSP_SoftwareDelay (1, BSP_DELAY_UNITS_MILLISECONDS); receive_timer_idle_isr();// Professional RX--independent task send_task();// Professional TX--independent task }#if BSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter();#endif}
/*******************************************************************************************************************//** * This function is called at various points during the startup process. This implementation uses the event that is * called right before main() to set up the pins. * * @param[in] event Where at in the start up process the code is currently at **********************************************************************************************************************/void R_BSP_WarmStart(bsp_warm_start_event_t event){ if (BSP_WARM_START_RESET == event) {#if BSP_FEATURE_FLASH_LP_VERSION != 0
/* Enable reading from data flash. */ R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and * C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */#endif }
if (BSP_WARM_START_POST_C == event) { /* C runtime environment and system clocks are setup. */
/* Configure pins. */ R_IOPORT_Open (&g_ioport_ctrl, &IOPORT_CFG_NAME); }}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADERBSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable (){
}FSP_CPP_FOOTER
#endif


APP Code

// RX needs to rely heavily on the protocol to specify LEN to capture a complete data packet
#include "hal_data.h"
FSP_CPP_HEADER void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/************************************************LOG**************************************/
/************************************************LOG**************************************/
/************************************************LOG**************************************/
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "SEGGER_RTT.h"
#define LOG(...) SEGGER_RTT_printf(0 ,##__VA_ARGS__);
#define SHOWME LOG("[%s]\r\n",__FUNCTION__);
void LOG_ARRY(char *name,uint8_t *A,uint8_t len){ SEGGER_RTT_printf(0, "%s ",name); for (size_t i = 0; i < len; i++) { SEGGER_RTT_printf(0, "0X%02X ",A[i]); } SEGGER_RTT_printf(0, "\r\n");}
void LOG_INIT(void){ SEGGER_RTT_Init(); R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS); SEGGER_RTT_printf(0, "Hello MY89003 APP"); LOG("World\r\n");}
/************************************************LED**************************************/
/************************************************LED**************************************/
/************************************************LED**************************************/
void Led_Ctl(uint8_t flag){ if(flag){ R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_LOW); }else{ R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_HIGH); }}
void Led_Blink(void){ static char flag=0; Led_Ctl(flag); flag = !flag;}
/************************************************CAN**************************************/
/************************************************CAN**************************************/
/************************************************CAN**************************************/
#define APP_ERR_TRAP() __asm("BKPT #0\n") /* trap upon the error */fsp_err_t err = FSP_SUCCESS;/* Flags to be set in Callback function */bool b_canfd_tx_complete = false;bool b_canfd_rx_complete = false;bool b_canfd_err_status = false;/* CANFD RX and TX variables */can_frame_t g_can_tx_frame;can_frame_t g_can_rx_frame;
#define SAY_MY_NAME 0X60
/* Variable to store rx frame status info*/can_info_t can_rx_info ={ .error_code = 0, .error_count_receive = 0, .error_count_transmit = 0, .rx_fifo_status = 0, .rx_mb_status = 0, .status = 0,};
//* Set filter: only messages with ID 0X0060 will be accepted by my MCU, otherwise the lower-level CAN will discard it directly!const canfd_afl_entry_t p_canfd0_afl[CANFD_CFG_AFL_CH0_RULE_NUM] ={ { .id = { .id = SAY_MY_NAME, .frame_type = CAN_FRAME_TYPE_DATA, .id_mode = CAN_ID_MODE_STANDARD, }, .mask = { .mask_id = 0x7F0, .mask_frame_type = 0, .mask_id_mode = 1, }, .destination = { .minimum_dlc = CANFD_MINIMUM_DLC_0, .fifo_select_flags = CANFD_RX_FIFO_0, }, },};
void CAN_Init(void){SHOWME err = R_CANFD_Open(&g_canfd0_ctrl, &g_canfd0_cfg); if(FSP_SUCCESS != err) { APP_ERR_TRAP(); }}
// The PC is agreed to transmit a maximum of 1K of file each time#define OTA_ONE_STEP 1024
// Each packet of data has 8 bytes (T and L) in front of the data#define HEADLEN 8
// The final container for a frame of datauint8_t gloableCANRxBuf[OTA_ONE_STEP+HEADLEN];uint16_t gloableCANRxBufLen=0;
// How to send? Slow sending// MCU needs to send out a frame of data container considering that MCUTX is not very large, a container of 200 is sufficient; each time the lower-level CAN sends a maximum of 8 bytes#define CAN_TX_BUFFER_LENGTH 200uint8_t gloableCANTxBuf[CAN_TX_BUFFER_LENGTH];uint16_t gloableNumNeedTxCAN = 0;uint8_t gloableNumTxedCAN=0;
// How to receive? Use protocol to identify when a frame is completely received// Once the LEN is detected, process it; otherwise, do nothingvoid receive_timer_idle_isr(void){ handle_gloableCANRxBuf();}
void CANSend_Max_8Byte(uint8_t* data,uint8_t len){// Note for parameters: len<=8 memcpy((uint8_t*)&g_can_tx_frame.data[0], data, len); g_can_tx_frame.id = SAY_MY_NAME; g_can_tx_frame.id_mode = CAN_ID_MODE_STANDARD; g_can_tx_frame.type = CAN_FRAME_TYPE_DATA; g_can_tx_frame.data_length_code = len;//8; g_can_tx_frame.options = 0; err = R_CANFD_Write(&g_canfd0_ctrl, 0, &g_can_tx_frame); if(FSP_SUCCESS != err) { APP_ERR_TRAP(); }}
void send_task(void){ if(gloableNumNeedTxCAN==0) return;// No work to do if(b_canfd_tx_complete != true) return;// Not finished sending, refuse to execute, wait for the next cycle to execute
// Determine whether to send 8 or 4 uint8_t numAllLeft = gloableNumNeedTxCAN - gloableNumTxedCAN; uint8_t numNowSend = 0; if (numAllLeft >= 8) numNowSend = 8; else numNowSend = numAllLeft;
// Actually send out uint8_t arr[8] = {0}; for (int i = 0;i < numNowSend;i++) arr[i] = gloableCANTxBuf[i+ gloableNumTxedCAN]; CANSend_Max_8Byte(arr, numNowSend); b_canfd_tx_complete = false;
// Update the number of TX sent gloableNumTxedCAN += numNowSend;
LOG("gloableNumNeedTxCAN = %d gloableNumTxedCAN=%d numNowSend=%d\r\n",gloableNumNeedTxCAN,gloableNumTxedCAN,numNowSend); if(gloableNumTxedCAN == gloableNumNeedTxCAN) { gloableNumNeedTxCAN = 0; gloableNumTxedCAN = 0; }}
/******************* OTA Structured Function for RX Reception Service **********************/typedef struct _otaStartType{ uint32_t reboot; uint32_t filesize;}otaStartType;otaStartType otaStart;typedef struct _otaStartingType{ int addr; int len;}otaStartingType;otaStartingType otaStarting;bool sendOtaAsk(otaStartingType info){ // Process the Nth packet, publish request, pack data T is hardcoded to 0002 char body[100]={0}; char head[8]={0}; char bodylen=0;
bodylen=sprintf(body,"%s%d,%s%d}","{\"addr\":", \\ info.addr, \\ "\"len\":", \\ info.len);
LOG("bodylen=%d [%s]\r\n",bodylen,body);
char *type="0002"; sprintf(head,"%s%04X",type,bodylen);
LOG("head=[%s]\r\n",head);
// Asynchronously send memset(gloableCANTxBuf,0,CAN_TX_BUFFER_LENGTH); // Don't know why, the following assignment always shows an exception, body is tampered with!! Not studying for now //memcpy(&gloableCANTxBuf[0],head,8); //memcpy(&gloableCANTxBuf[8],body,len); // Still honestly repack TLV sprintf(gloableCANTxBuf,"%s%04X%s%d,%s%d}",type,bodylen,"{\"addr\":", \\ info.addr, \\ "\"len\":", \\ info.len);
LOG("gloableCANTxBuf=[%s]\r\n",gloableCANTxBuf);
// Trigger send task gloableNumNeedTxCAN = (bodylen+8); b_canfd_tx_complete = true; return true; }
char saveOtaData(uint8_t *data, uint16_t len);void ota_config_clear(void);
#define BHEX(A) ((A) <= '9'? (A) - '0' : ((A) >= 'A' && (A) <= 'F'? (A) + 10 - 'A' : (A) + 10 - 'a'))#define TYPE (BHEX(gloableCANRxBuf[0])<<12) | (BHEX(gloableCANRxBuf[1])<<8) | (BHEX(gloableCANRxBuf[2])<<4) | (BHEX(gloableCANRxBuf[3]))#define LEN (BHEX(gloableCANRxBuf[4])<<12) | (BHEX(gloableCANRxBuf[5])<<8) | (BHEX(gloableCANRxBuf[6])<<4) | (BHEX(gloableCANRxBuf[7]))#define VALUE &gloableCANRxBuf[8];
// Not using LENvoid handle_gloableCANRxBuf(void){ int needLen = gloableCANRxBufLen-HEADLEN; int trueLen = LEN; //LOG("needLen=%d trueLen=%d\r\n",needLen,trueLen);
if(trueLen != needLen) return;// Length mismatch
// Copy out and print char headStr[HEADLEN+1] = {0}; memcpy(headStr,gloableCANRxBuf,8); LOG("head [%s] \r\n",headStr);
uint8_t *body = VALUE; // MCU is an absolute slave; upon receiving a message, it must respond with a message switch (TYPE){ case 0x0001:// Received file information frame A: record file size B: RESET LOG("body=%s\r\n",body);/* body={ "filesize": 130012, "md5": "XXX", "ver": 4}*/ saveOtaCfg(body); NVIC_SystemReset(); break;
case 0x0003: LOG("\r\nNEVER IN APP WORLD\r\n"); break;
} gloableCANRxBufLen=0;}
/* Callback function from GUI interrupt service function; CAN received will run in here; CAN sent will also run in here*/void canfd0_callback(can_callback_args_t *p_args){ /* TODO: add your own code here */
//LOG("0X%04X \r\n",p_args->event); Led_Blink(); switch (p_args->event) { case CAN_EVENT_TX_COMPLETE: { b_canfd_tx_complete = true; LOG_ARRY("CAN_EVENT_TX_COMPLETE",g_can_tx_frame.data,g_can_tx_frame.data_length_code); break; } case CAN_EVENT_RX_COMPLETE: {
b_canfd_rx_complete = true; memcpy(&g_can_rx_frame, &p_args->frame, sizeof(can_frame_t)); LOG_ARRY("CAN_EVENT_RX_COMPLETE",g_can_rx_frame.data,g_can_rx_frame.data_length_code);
memcpy(&gloableCANRxBuf[gloableCANRxBufLen],g_can_rx_frame.data,g_can_rx_frame.data_length_code);
gloableCANRxBufLen+=g_can_rx_frame.data_length_code;
//LOG_ARRY("gloableCANRxBuf",gloableCANRxBuf,gloableCANRxBufLen); break; } case CAN_EVENT_ERR_WARNING: // Error warning event case CAN_EVENT_ERR_PASSIVE: // Error passive event case CAN_EVENT_ERR_BUS_OFF: // Error Bus Off event case CAN_EVENT_BUS_RECOVERY: // Bus recovery error event case CAN_EVENT_MAILBOX_MESSAGE_LOST: // Overwrite/overrun error event case CAN_EVENT_ERR_BUS_LOCK: // Bus lock detected (32 consecutive dominant bits). case CAN_EVENT_ERR_CHANNEL: // Channel error has occurred. case CAN_EVENT_TX_ABORTED: // Transmit abort event. case CAN_EVENT_ERR_GLOBAL: // Global error has occurred. case CAN_EVENT_FIFO_MESSAGE_LOST: // Transmit FIFO is empty. case CAN_EVENT_TX_FIFO_EMPTY: // Transmit FIFO is empty. { b_canfd_err_status = true; // Set flag bit break; } }}
/************************************************FLASH**************************************/
/************************************************FLASH**************************************/
/************************************************FLASH**************************************/
/* Code Flash */#define FLASH_HP_CF_BLOCK_SIZE_32KB (32*1024U) /* Block Size 32 KB */#define FLASH_HP_CF_BLOCK_SIZE_8KB (8*1024U) /* Block Size 8KB */
#define FLASH_HP_CF_BLOCK_0 (0x00000000U) /* 8 KB: 0x00000000 - 0x00001FFF */#define FLASH_HP_CF_BLOCK_1 (0x00002000U) /* 8 KB: 0x00002000 - 0x00003FFF */#define FLASH_HP_CF_BLOCK_2 (0x00004000U) /* 8 KB: 0x00004000 - 0x00005FFF */#define FLASH_HP_CF_BLOCK_3 (0x00006000U) /* 8 KB: 0x00006000 - 0x00007FFF */#define FLASH_HP_CF_BLOCK_4 (0x00008000U) /* 8 KB: 0x00008000 - 0x00009FFF */#define FLASH_HP_CF_BLOCK_5 (0x0000A000U) /* 8 KB: 0x0000A000 - 0x0000BFFF */#define FLASH_HP_CF_BLOCK_6 (0x0000C000U) /* 8 KB: 0x0000C000 - 0x0000DFFF */#define FLASH_HP_CF_BLOCK_7 (0x0000E000U) /* 8 KB: 0x0000E000 - 0x0000FFFF */#define FLASH_HP_CF_BLOCK_8 (0x00010000U) /* 32 KB: 0x00010000 - 0x00017FFF */#define FLASH_HP_CF_BLOCK_9 (0x00018000U) /* 32 KB: 0x00018000 - 0x0001FFFF */
/* Data Flash */#define FLASH_HP_DF_BLOCK_SIZE_64B (64U) /* Block Size 64 Byte */#define FLASH_HP_DF_BLOCK_0 (0x08000000U) /* Size 64 B: 0x08000000U - 0x0800003F */#define FLASH_HP_DF_BLOCK_1 (0x08000040U) /* Size 64 B: 0x08000040U - 0x0800007F */#define FLASH_HP_DF_BLOCK_2 (0x08000080U) /* Size 64 B: 0x08000080U - 0x080000BF */#define FLASH_HP_DF_BLOCK_3 (0x080000C0U) /* Size 64 B: 0x080000C0U - 0x080000FF */#define FLASH_HP_DF_BLOCK_63 (0x08000FC0U) /* Size 64 B: 0x08000FCOU - 0x08000FFF */#define FLASH_HP_DF_BLOCK_64 (0x08001000U) /* Size 64 B: 0x08001000U - 0x0800103F */
// Note: The minimum write granularity of code flash is 128 bytes // If the parameter passed to R_FLASH_HP_Write is 4 or 64, it will crash! // So here prepare a 128 array to maintain the minimum write volatile uint8_t g_src_uint8[128]={0};
// Data flash requires callbacks, is a state machine, needs interrupts, needs callbacks // Code flash is blocking, do not need interrupts, do not need callbacks // This article does not use data flash, so no need to write callback functionsvoid my_flash_callback(flash_callback_args_t *p_args){}
void flash_init(void){ /* Open the flash lp instance. */ err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg); assert(FSP_SUCCESS == err);}
#define APPLICATION_CONFIG_ADDRESS FLASH_HP_CF_BLOCK_7 /* 8 KB: 0x0000E000 - 0x0000FFFF */
// This article uses the last 8K of code flash; erasing it will make it all 0XFFFFFFFF in writing a U32 void x_flash_write(void){ flash_result_t blank_check_result;
__disable_irq();
err = R_FLASH_HP_Erase(&g_flash0_ctrl, APPLICATION_CONFIG_ADDRESS, 1); assert(FSP_SUCCESS == err); err = R_FLASH_HP_BlankCheck(&g_flash0_ctrl, APPLICATION_CONFIG_ADDRESS, FLASH_HP_CF_BLOCK_SIZE_8KB, &blank_check_result); assert(FSP_SUCCESS == err); assert(FLASH_RESULT_BLANK == blank_check_result);
memcpy(g_src_uint8, (uint8_t *)&otaStart.filesize,4); err = R_FLASH_HP_Write(&g_flash0_ctrl, (uint32_t) g_src_uint8, APPLICATION_CONFIG_ADDRESS, 128); assert(FSP_SUCCESS == err);
__enable_irq();}
// Write U32 and write otaStart.filesize to flash as a bond void ota_config_write(void){ x_flash_write();}
/*******************************************************************************************************************//** * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function * is called by main() when no RTOS is used. **********************************************************************************************************************/void hal_entry(void){ /* TODO: add your own code here */ LOG_INIT(); CAN_Init(); flash_init(); while(1){#if 1 R_BSP_SoftwareDelay (1, BSP_DELAY_UNITS_MILLISECONDS); receive_timer_idle_isr();// Professional RX--independent task 【This APP does not have TX functionality】#else R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS); receive_timer_idle_isr(); Led_Blink();#endif }#if BSP_TZ_SECURE_BUILD /* Enter non-secure code */ R_BSP_NonSecureEnter();#endif}
/*******************************************************************************************************************//** * This function is called at various points during the startup process. This implementation uses the event that is * called right before main() to set up the pins. * * @param[in] event Where at in the start up process the code is currently at **********************************************************************************************************************/void R_BSP_WarmStart(bsp_warm_start_event_t event){ if (BSP_WARM_START_RESET == event) {#if BSP_FEATURE_FLASH_LP_VERSION != 0
/* Enable reading from data flash. */ R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and * C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */#endif }
if (BSP_WARM_START_POST_C == event) { /* C runtime environment and system clocks are setup. */
/* Configure pins. */ R_IOPORT_Open (&g_ioport_ctrl, &IOPORT_CFG_NAME); }}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADERBSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable (){
}FSP_CPP_FOOTER
#endif