最近开发的一个小项目须要支持蓝牙在线升级,今天便详细地了解一番。蓝牙在线升级的方式,流程如图 要实现这一功能,必须经过单片机的串口IAP在线升级功能。
因此要实现IAP功能,固件程序须分为两个代码,即引导程序(BootLoader)和用户程序(APP)。 2.STM32程序的启动方式 3. Flash在STM32内存空间的定义 4.引导程序和用户程序内存空间的划分 而咱们将FLash的内存空间分为引导程序和用户程序,其0x0800 0000~0x0800 2000,8KByte的空间做为BootLoader,将0x0800 2000 ~0x0801 FFFF,共120KByte做为用户空间(STM32F103C8T6实际只有64KByte Flash,用户空间为56KByte)。
5.引导程序的设计 extern uint8_t usart_buf[1024+8]; //数据长度(2B) 数据(1KB) 序号(2B) [CRC(4B)] extern uint16_t buf_cnt; extern uint8_t bootStatus; void USART1_IRQHandler(void) { uint8_t temp = 0; if( (USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET) ) { temp = (uint8_t)USART_ReceiveData(USART1); if(++buf_cnt < (1024+8) && ((buf_cnt & 0x8000) != 0x8000)) { usart_buf[buf_cnt - 1] = temp; } else buf_cnt |= 0x8000; } } (2)在main函数中,对接收到的数据进行判断设计 if(flag) //是否串口是否有接收到数据 { printf("开始更新固件r\n"); // 判断APP程序的起始地址是否为0X08XXXXXX if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000) { iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新 FLASHFLASH代码 printf("固件更新成功\r\n"); } else { printf("非固件程序\r\n"); } flag = 0; (3)若是检测到APP程序,进入FLASH烧写函数 // 从addr起烧录程序 void Iap_Write(uint32_t addr) { uint16_t temp = 0; uint16_t data_size = 0; //烧录大小 uint16_t data_len = 0; //数据长度 uint16_t index = 0; //数据块索引 uint32_t addr_now = addr; //写入地址 uint8_t data_write[1024] = {0}; //数据缓冲 //准备烧录,数据大小为1K while(1) { while((buf_cnt & 0x8000) == 0); //等待数据接收完毕 //解析数据 data_len = (uint16_t)usart_buf[1] << 8 | usart_buf[0]; //获取data有效长度 for(temp = 2; temp < data_len + 2; temp++) //从第二位开始拷贝数据 { data_write[temp - 2] = usart_buf[temp]; } index = usart_buf[1024 + 3] << 8 + usart_buf[1024 + 2]; //获取索引 //开始写数据 if(data_len < 1024) //写入剩下的数据 { if(data_len % 2 != 0)data_len += 1; STMFLASH_Write(addr_now, (uint16_t *)data_write, data_len / 2); // data_size += 1; STMFLASH_Write(IAP_INFO, &data_len, 1); putString("OK\n"); break; } else { STMFLASH_Write(addr_now, (uint16_t*)data_write, data_len / 2); data_size += 1; addr_now += 1024; //下一个1K buf_cnt = 0; for(temp = 0; temp < USART_BUF_SIZE; temp++)usart_buf[temp] = 0; putString("Next\n"); } } //烧录完成 //清空串口缓存 buf_cnt = 0; for(temp = 0; temp < USART_BUF_SIZE; temp++)usart_buf[temp] = 0; } (4)烧录好APP程序后,引导程序将PC指针地址指向APP程序的起始地址,即0x0800 2000;要对PC指针进行操做,需使用MSR汇编指令来操做。 //设置栈顶地址 //addr:栈顶地址 __asm void MSR_MSP(u32 addr) { MSR MSP, r0 //set Main Stack value BX r14 } 而后执行PC指针跳转函数 void Iap_load(uint32_t addr) { if(((*(vu32*)addr) & 0x2FFE0000) == 0x20000000) { jump = (iapfun) *(vu32*)(addr + 4); //强制转化为函数 MSR_MSP(*(vu32*)addr); jump(); } else { printf("Error\n"); while(1); } } (5)最后,main函数的实现就很简单了,以轮询的方式读取串口数据,而后烧写FLASH,最后跳转至APP程序 while(1) { while((readBootTime() != 0) && (flag == 0)) //以轮询的方式读取串口 { if(Iap_wait() == 8) { USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); flag = 1; } } if(flag) { IAP_WRITE(); flag = 0; } else IAP_LOAD(); //跳转至APP程序 } 6.用户程序APP的实现 (2) 设置APP程序的中断向量表的偏移量 SCB->VTOR = FLASH_BASE | 0x2000; 以上设置完成以后,点击rebuild按钮从新编译,生成LED.hex |
|