上文已经分析了MCU上电的启动流程。本文尝试编写一个简单的bootloader以及一个实现CAN通信的APP,为我们下一步尝试编写一个通过CAN通信实现刷写APP功能的bootloader作铺垫。
下面直接上干货。
看一下bootloader工程的结构,非常的简单,实现代码也非常简单。
main.h
#ifndef _MAIN_H
#define _MAIN_H
#define APPLICATION_ADDRESS (uint32_t)0x08004000//APP工程的起始地址
typedef void (*pFunction)(void);
#endif
main.c
#include "main.h"
#include "ac78xx_can.h"
#include "ac78xx_rtc.h"
#include "ac78xx_gpio.h"
uint32_t JumpAddress = 0;
uint8_t JumpToApplication_Flag = 0;
pFunction JumpToApplication;
int main(void)
{
JumpToApplication_Flag = 1;
if(JumpToApplication_Flag == 1)
{
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);//JumpAddress = 0x08004004 此处存放的是APP工程的Reset_Handler的中断向量
JumpToApplication = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);//此处是设置SP指针指向0x08004000处的数据即APP工程的栈顶地址
JumpToApplication();
}
while(1)
{
}
}
此上就是简单实现了一个bootloader,能够跳转到我们的APP应用区。
下面看一下APP工程。
本APP简单实现了CAN的收发功能,展示下核心代码(协议报文自定义,采用motorola格式)
int32_t CAN1_IRQnCallBack(uint32_t event, uint32_t wparam, uint32_t lparam)
{
uint8_t i = 0;
if (event & CAN_EVENT_RECVMSG)
{
while(CAN_IsMsgInReceiveBuf((CAN_Type*)lparam))
{
CAN_MessageRead((CAN_Type*)lparam, &RxMessage_Can1);
if(RxMessage_Can1.IDE == 0)
{
if(RxMessage_Can1.ID == TEST_ADDRESS_333)
{
Can_Rec_Debug_Frame_Flag = 1;
Rec_Byte_555_0 = (uint8_t) RxMessage_Can1.Data[0];
Rec_Byte_555_1 = (uint8_t) RxMessage_Can1.Data[1];
Rec_Byte_555_2 = (uint8_t) RxMessage_Can1.Data[2];
Rec_Byte_555_3 = (uint8_t) RxMessage_Can1.Data[3];
Rec_Byte_555_4 = (uint8_t) RxMessage_Can1.Data[4];
Rec_Byte_555_5 = (uint8_t) RxMessage_Can1.Data[5];
Rec_Byte_555_6 = (uint8_t) RxMessage_Can1.Data[6];
Rec_Byte_555_7 = (uint8_t) RxMessage_Can1.Data[7];
Debug_Frame_Management();
send_can_message_task();
}
}
else if(RxMessage_Can1.IDE == 1)
{
}
}
}
return 1;
}
extern void Debug_Frame_Management(void)
{
uint16_t temp = 0;
if(Can_Rec_Debug_Frame_Flag == 1)
{
temp = Rec_Byte_555_0;
temp = (temp << 8) + Rec_Byte_555_1;
message.vol = temp / 10;
temp = Rec_Byte_555_2;
temp = (temp << 8) + Rec_Byte_555_3;
message.curr = temp / 100 - 300;
}
}
extern void send_can_message_task(void)
{
if(Can_Rec_Debug_Frame_Flag ==1)
{
uint8_t i = 0;
TxMessage.ID = ACK_DBG_ADDRESS_666;
TxMessage.RTR = 0;
TxMessage.IDE = 0;
TxMessage.DLC = 8;
TxMessage.Data[i++] = ((message.vol * 10) >> 8) & 0xFF;
TxMessage.Data[i++] = (message.vol * 10) & 0xFF;
TxMessage.Data[i++] = (((message.curr + 300) * 100) >> 8 ) & 0xFF;
TxMessage.Data[i++] = ((message.curr + 300) * 100) & 0xFF;
TxMessage.Data[i++] = Rec_Byte_555_4;
TxMessage.Data[i++] = Rec_Byte_555_5;
TxMessage.Data[i++] = Rec_Byte_555_6;
TxMessage.Data[i++] = Rec_Byte_555_7;
if(CAN_MessageSend(CAN1, &TxMessage, TRANSMIT_SECONDARY) != 0)
{
}
Can_Rec_Debug_Frame_Flag = 0;
}
}
上位机使用的PCAN-Explorer,报文dbc如下:
以下是测试结果:
测试结果表明MCU上电后能够通过编写的简单的bootloader跳转至APP区且CAN通信正常,下一步我们将尝试编写更复杂的支持通过CAN通信刷写APP的bootloader。