说明
使用qt编写上位机,电脑通过USB转串口工具与单片机连接,使用自定义协议进行bin文件传送
协议
总共有四帧协议:
type1:上位机点击“下载文件”按钮后,会连续发送这帧命令,查询STM32是否准备好
type10:上位机下发文件,一帧最多下发4096字节数据
type10:在APP程序中STM32接受到type1帧后软件复位进入bootload程序,bootload程序接收到type1帧后发送type2帧告诉上位机准备完成,等待下发文件
type20:STM32接收到一帧数据后写入到FLASH,返回成功或失败响应
STM32程序部分
bootload程序
程序跳转部分代码
#define APP_ADDR 0x8020000 /* 应用程序首地址定义 */
static void iap_load_app(void)
{
void (*AppMemBootJump)(void); /* 声明一个函数指针 */
/* 关闭全局中断 */
DISABLE_INT();
/* 关闭滴答定时器,复位到默认值 */
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 设置所有时钟到默认状态,使用 HSI 时钟 */
HAL_RCC_DeInit();
/* 关闭所有中断,清除所有中断挂起标志 */
for (int i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* 使能全局中断 */
ENABLE_INT();
/* 设置重映射到系统 Flash */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* 跳转到APP程序,首地址是 MSP,地址+4 是复位中断服务程序地址 */
AppMemBootJump = (void (*)(void)) (*((uint32_t *) (APP_ADDR + 4)));
/* 设置朱堆栈指针 */
__set_MSP(*(uint32_t *)APP_ADDR);
/* 在 RTOS 工程,这条语句很重要,设置为特权级模式,使用 MSP 指针 */
__set_CONTROL(0);
/* 跳转至APP */
AppMemBootJump();
/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
while (1)
{
}
}
协议解析和程序写入
/* 进入主程序循环体 */
while (1)
{
static int tim_100ms = 0;
static int mode = 0;
static int tim_1000ms = 0;
uint8_t com6_rx_data = 0;
uint16_t crc = 0;
if (tim_1ms_flag == 1) /* 1ms执行一次 */
{
tim_1ms_flag = 0;
if (mode == 0)
{
if (++tim_1000ms > 1000) iap_load_app(); /* 超时未进入下载模式,跳转到APP */
}
if (++tim_100ms > 100) /* 指示灯闪缩 */
{
tim_100ms = 0;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
while(comGetChar(COM6, (uint8_t*)&com6_rx_data))
{
tRx.buf[tRx.cnt++] = com6_rx_data;
switch (tRx.cnt)
{
case 1: {
if (com6_rx_data != 0xEE) tRx.cnt = 0;
} break;
case 2: {
if (com6_rx_data != 0xAA) tRx.cnt = 0;
} break;
case 7: {
tRx.type = tRx.buf[2];
tRx.num = (tRx.buf[3] << 8) + tRx.buf[4];
tRx.lenght = (tRx.buf[5] << 8) + tRx.buf[6];
} break;
}
if (tRx.cnt == tRx.lenght + 9) /* 一帧数据 */
{
tRx.cnt = 0;
crc = CRC16_Modbus(tRx.buf, tRx.lenght + 7); /* 校验帧头+帧类型+帧号+数据长度+数据 */
if (crc == (tRx.buf[tRx.lenght + 9 - 2] << 8) + tRx.buf[tRx.lenght + 9 - 1]) /* 校验成功 */
{
if (tRx.type == 0x01)
{
mode = 1; /* 进入下载模式 */
send_ack(0x10, 0x00, 0x00, 0x00); /* boot程序接收准备应答 */
}
else if (tRx.type == 0x02)
{
if (bsp_WriteCpuFlash(add, tRx.buf + 7, tRx.lenght) == 0)
{
add += tRx.lenght;
send_ack(0x20, tRx.num, 0x01, 0x01); /* 文件下载成功应答 */
}
else
{
send_ack(0x20, tRx.num, 0x01, 0x02); /* 文件下载失败应答 */
}
if (tRx.num == 0xffff)
{
tim_1000ms = 0;
mode = 0;
}
}
}
}
}
}
协议应答
/*
*********************************************************************************************************
* 功能说明: 应答
*********************************************************************************************************
*/
static void send_ack(uint8_t type, uint16_t num, uint16_t length, uint8_t data)
{
uint8_t send_buf[20] = {0};
uint8_t i =0;
send_buf[i++] = 0xEE;
send_buf[i++] = 0xAA;
send_buf[i++] = type;
send_buf[i++] = num>>8;
send_buf[i++] = num;
send_buf[i++] = length>>8;
send_buf[i++] = length;
for (int a=0; a<length; a++)
{
send_buf[i++] = data;
}
uint16_t crc = CRC16_Modbus(send_buf, i);
send_buf[i++] = crc>>8;
send_buf[i++] = crc;
comSendBuf(COM6, (uint8_t *)&send_buf, i);
}
APP程序
接收进入b