https://blog.csdn.net/bobbat/article/details/45061413
在之前的《STM32串口IAP》一文中,通过传输数据流来升级程序,但是这种"裸"数据的传输方式存在这许多的问题,比如它没有容错机制,不能保证数据的正确传输,还比如说它无法获知升级文件的信息,导致它在判断何时停止接收数据上“犹豫不决”。正式为了解决上面的问题,才引进了YModem协议。
/************************************************************* Function : IAP_Init Description: IAP初始化函数,初始化串口1 Input : none return : none *************************************************************/ void IAP_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(COM1_RCC, ENABLE);//使能 USART2 时钟 RCC_APB2PeriphClockCmd(COM1_GPIO_RCC, ENABLE);//使能串口2引脚时钟 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置 USART2 的Tx 引脚类型为推挽式的 GPIO_InitStructure.GPIO_Pin = COM1_TX_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(COM1_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//配置 USART2 的Rx 为输入悬空 GPIO_InitStructure.GPIO_Pin = COM1_RX_PIN; GPIO_Init(COM1_GPIO_PORT, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200;//设置波特率为115200 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//设置数据位为8位 USART_InitStructure.USART_StopBits = USART_StopBits_1;//设置停止位为1位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //没有硬件流控 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送与接收 USART_Init(COM1,&USART_InitStructure);//串口2相关寄存器的配置 USART_Cmd(COM1,ENABLE);//使能串口2 }
/************************************************************* Function : IAP_SerialSendByte Description: 串口发送字节 Input : c-要发送的字节 return : none *************************************************************/ void IAP_SerialSendByte(u8 c) { USART_SendData(COM1, c); while (USART_GetFlagStatus(COM1, USART_FLAG_TXE) == RESET) {} } /************************************************************* Function : IAP_SerialSendStr Description: 串口发送字符串 Input : none return : none *************************************************************/ void IAP_SerialSendStr(u8 *s) { while(*s != '\0') { IAP_SerialSendByte(*s); s++; } } /************************************************************* Function : IAP_SerialGetByte Description: 接收一个字节数据 Input : none return : 返回结果值,0-没有接收到数据;1-接收到数据 *************************************************************/ u8 IAP_SerialGetByte(u8 *c) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) { *c = USART_ReceiveData(USART1); return 1; } return 0; }
可以看到串口接收函数IAP_SerialGetByte()仅仅是查询下是否有数据接收到,但无论是否接收到数据,程序都不会在这个函数上阻塞。所以为了实现阻塞的效果,还需要稍微做下文章,下面就提前讲讲如何在IAP_SerialGetByte()的基础上修改成阻塞的功能,如下获取一个用户输入键值的函数:/************************************************************* Function : IAP_GetKey Description: 获取键入值 Input : none return : 返回键值 *************************************************************/ u8 IAP_GetKey(void) { u8 data; while(!IAP_SerialGetByte(&data)){ } return data; }
然后再设计Bootload的界面,更《STM32串口IAP》一文给出的界面基本上一样,如下:/************************************************************* Function : IAP_ShowMenu Description: 显示菜单界面 Input : none return : none *************************************************************/ void IAP_ShowMenu(void) { IAP_SerialSendStr("\r\n+================(C) COPYRIGHT 2014 Ziye334 ================+"); IAP_SerialSendStr("\r\n| In-Application Programing Application (Version 1.0) |"); IAP_SerialSendStr("\r\n+----command----+-----------------function------------------+"); IAP_SerialSendStr("\r\n| 1: FWUPDATA | Update the firmware to flash by YModem |"); IAP_SerialSendStr("\r\n| 2: FWDWLOAD | Download the firmware from Flash by YModem|"); IAP_SerialSendStr("\r\n| 3: FWERASE | Erase the current firmware |"); IAP_SerialSendStr("\r\n| 4: BOOT | Excute the current firmware |"); IAP_SerialSendStr("\r\n| 5:REBOOT | Reboot |"); IAP_SerialSendStr("\r\n| ?: HELP | Display this help |"); IAP_SerialSendStr("\r\n+===========================================================+"); IAP_SerialSendStr("\r\n\r\n"); IAP_SerialSendStr("STM32-IAP>>"); }
界面与之前相比,唯一的区别在于多了一个FWDWLOAD的选项,提供用户从STM32上下载升级文件的功能。接下去讲讲这些功能的实现。/************************************************************* Function : IAP_DisableFlashWPR Description: 关闭flash的写保护 Input : none return : none *************************************************************/ void IAP_DisableFlashWPR(void) { u32 blockNum = 0, UserMemoryMask = 0; blockNum = (IAP_ADDR - FLASH_BASE_ADDR) >> 12; //计算flash块 UserMemoryMask = ((u32)(~((1 << blockNum) - 1)));//计算掩码 if((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask)//查看块所在区域是否写保护 { FLASH_EraseOptionBytes ();//擦除选择位 } } s8 IAP_UpdataParam(s32 *param) { u32 i; u32 flashptr = IAP_PARAM_ADDR; FLASH_Unlock();//flash上锁 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清除flash相关标志位 for(i = 0; i < IAP_PARAM_SIZE; i++) { FLASH_ProgramWord(flashptr + 4 * i, *param); if(*(u32 *)(flashptr + 4 * i) != *param) { return -1; } param++; } FLASH_Lock();//flash解锁 return 0; } /************************************************************* Function : IAP_UpdataProgram Description: 升级程序 Input : addr-烧写的地址 size-大小 return : 0-OK 1-error *************************************************************/ s8 IAP_UpdataProgram(u32 addr, u32 size) { u32 i; static u32 flashptr = IAP_ADDR; FLASH_Unlock();//flash上锁 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清除flash相关标志位 for(i = 0; i < size; i += 4) { FLASH_ProgramWord(flashptr, *(u32 *)addr);//烧写1个字 if(*(