分享ST系列MCU利用UART实现刷机经验

2019-07-20 04:59发布

本帖最后由 八度空间 于 2017-3-12 14:02 编辑

测试环境:STM32F302芯片
编译环境:MDK5.18
库版本:V1.2.2

一、想用UART对ST系列MCU进行刷机必须了解其协议
      官网(或者论坛www.stmcu.og)上下载协议说明文档AN3155
二、了解UART通讯参数
      打开AN3155文档,得到串口参数及bootloader运行动作
1.jpg
      解读后得到的信息为:1位起始位
                                    偶校验
                                    1位停止位
      有人会问了,数据位呢?0x7F,数据位就是8位啦,还用说呢,很遗憾告诉,你用8位数据位进行通讯,百分百出错,血的教训,必须9位数据位
三、波特率设置
      文档有最大和最小值说明
2.jpg
      直接使用最大115200了,提高通讯效率了
四、本工程配置,使用了DMA进行,当然也有串口查询的方式,没有使用串口中断来做,大家自行处理中断方式就是了
[mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @name   : 控制管脚相关初始化
  *
  * @Brief  : none
  *
  * @Input  : bound:波特率
  *
  * @Output : none
  *
  * @Return : none
  *****************************************************************************
**/
void ST_UART_PRO_Init(u32 bound)
{
        USART_InitTypeDef USART_Init_Pro;
        #if ST_UART_PRO_DMA_EN
        DMA_InitTypeDef DMA_Init_Pro;
        NVIC_InitTypeDef NVIC_Init_Pro;
        
        RCC->AHBENR  |= RCC_AHBENR_DMA1EN;
        #endif
        
        RCC->AHBENR  |= RCC_AHBENR_GPIOBEN;
        RCC->AHBENR  |= RCC_AHBENR_GPIODEN;
        RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
        //
        //NRST控制管脚  PB.01
        //电源控制管脚  PB.15
        //BOOT0控制管脚 PD.11
        //
        GPIOB->MODER   &= ~(GPIO_MODER_MODER1 | GPIO_MODER_MODER15);
        GPIOB->OTYPER  &= ~(GPIO_OTYPER_OT_1 | GPIO_OTYPER_OT_15);
        GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR1 | GPIO_OSPEEDER_OSPEEDR15);
        GPIOB->PUPDR   &= ~(GPIO_PUPDR_PUPDR1 | GPIO_PUPDR_PUPDR15);
        
        GPIOB->MODER   |= (GPIO_MODER_MODER1_0 | GPIO_MODER_MODER15_0);
        GPIOB->PUPDR   |= (GPIO_PUPDR_PUPDR1_0 | GPIO_PUPDR_PUPDR15_0);
        
        GPIOD->MODER   &= ~GPIO_MODER_MODER11;
        GPIOD->OTYPER  &= ~GPIO_OTYPER_OT_11;
        GPIOD->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR11;
        GPIOD->PUPDR   &= ~GPIO_PUPDR_PUPDR11;
        
        GPIOD->MODER   |= GPIO_MODER_MODER11_0;
        GPIOD->PUPDR   |= GPIO_PUPDR_PUPDR11_0;
        //
        //管脚复用
        //
        GPIOB->AFR[1] &= ~(GPIO_AFRH_AFRH2 | GPIO_AFRH_AFRH3);
        GPIOB->AFR[1] |= 7<<8;  //AF7
        GPIOB->AFR[1] |= 7<<12;  //AF7
        //
        //TXD PB.10
        //RXD PB.11
        //
        GPIOB->MODER   &= ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11);
        GPIOB->OTYPER  &= ~(GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
        GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
        GPIOB->PUPDR   &= ~(GPIO_PUPDR_PUPDR10 | GPIO_PUPDR_PUPDR11);
        
        GPIOB->MODER   |= (GPIO_MODER_MODER10_1 | GPIO_MODER_MODER11_1);
        GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
        GPIOB->PUPDR   |= (GPIO_PUPDR_PUPDR10_0 | GPIO_PUPDR_PUPDR11_0);
        
        GPIOB->BSRR = (GPIO_BSRR_BS_15 | GPIO_BSRR_BS_1);  //打开副U电源,复位管脚置高,无复位事件
        GPIOD->BSRR = GPIO_BSRR_BR_11;  //BOOT0管脚置低
        
        #if ST_UART_PRO_DMA_EN
        //
        //配置串口DMA发送中断优先级
        //
        NVIC_Init_Pro.NVIC_IRQChannel                   = DMA1_Channel2_IRQn;
        NVIC_Init_Pro.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_Init_Pro.NVIC_IRQChannelSubPriority        = 1;
        NVIC_Init_Pro.NVIC_IRQChannelCmd                = ENABLE;
        NVIC_Init(&NVIC_Init_Pro);
        //
        //配置串口3中断优先级
        //
        NVIC_Init_Pro.NVIC_IRQChannel                   = USART3_IRQn;
//        NVIC_Init_Pro.NVIC_IRQChannelPreemptionPriority = 0;
//        NVIC_Init_Pro.NVIC_IRQChannelSubPriority        = 1;
//        NVIC_Init_Pro.NVIC_IRQChannelCmd                = ENABLE;
        NVIC_Init(&NVIC_Init_Pro);
        
        //
        //配置串口DMA发送通道
        //
        DMA_Cmd(DMA1_Channel2, DISABLE);
        DMA_DeInit(DMA1_Channel2);
        
        DMA_Init_Pro.DMA_PeripheralBaseAddr = (uint32_t)(&USART3->TDR);
        
        DMA_Init_Pro.DMA_MemoryBaseAddr     = (uint32_t)&ST_UART_PRO_TX_BUF;
        DMA_Init_Pro.DMA_DIR                = DMA_DIR_PeripheralDST;
        DMA_Init_Pro.DMA_M2M                = DMA_M2M_Disable;
        
        DMA_Init_Pro.DMA_BufferSize         = ST_UART_PRO_TX_RX_SIZE;
        DMA_Init_Pro.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
        DMA_Init_Pro.DMA_MemoryInc          = DMA_MemoryInc_Enable;
        DMA_Init_Pro.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        DMA_Init_Pro.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
        DMA_Init_Pro.DMA_Mode               = DMA_Mode_Normal;
        DMA_Init_Pro.DMA_Priority           = DMA_Priority_High;
        
        //
        //配置串口DMA接收通道
        //
        DMA_Init(DMA1_Channel2, &DMA_Init_Pro);
        DMA_ClearFlag(DMA1_FLAG_GL2);
        DMA_Cmd(DMA1_Channel2, DISABLE);
        
        DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);  //开启发送完成中断
        
        //
        //配置串口DMA接收通道
        //
        DMA_Cmd(DMA1_Channel3, DISABLE);
        DMA_DeInit(DMA1_Channel3);
        
        DMA_Init_Pro.DMA_PeripheralBaseAddr = (uint32_t)(&USART3->RDR);
        
        DMA_Init_Pro.DMA_MemoryBaseAddr     = (uint32_t)&ST_UART_PRO_RX_BUF;
        DMA_Init_Pro.DMA_DIR                = DMA_DIR_PeripheralSRC;
        DMA_Init_Pro.DMA_M2M                = DMA_M2M_Disable;
        
        DMA_Init_Pro.DMA_BufferSize         = ST_UART_PRO_TX_RX_SIZE;
        DMA_Init_Pro.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
        DMA_Init_Pro.DMA_MemoryInc          = DMA_MemoryInc_Enable;
        DMA_Init_Pro.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        DMA_Init_Pro.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
        DMA_Init_Pro.DMA_Mode               = DMA_Mode_Normal;
        DMA_Init_Pro.DMA_Priority           = DMA_Priority_High;
        
        DMA_Init(DMA1_Channel3, &DMA_Init_Pro);
        DMA_ClearFlag(DMA1_FLAG_GL3);
        DMA_Cmd(DMA1_Channel3, ENABLE);
        #endif
        //
        //串口初始化
        //
//        USART_DeInit(USART3);
        //
        //初始化串口参数
        //
        USART_Init_Pro.USART_BaudRate            = bound;
        USART_Init_Pro.USART_WordLength          = USART_WordLength_9b;
        USART_Init_Pro.USART_StopBits            = USART_StopBits_1;
        USART_Init_Pro.USART_Parity              = USART_Parity_Even;
        USART_Init_Pro.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_Init_Pro.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
        USART_Init(USART3, &USART_Init_Pro);
        
        USART_Cmd(USART3, ENABLE);
        
        //
        //开启串口中断接收
        //
        #if ST_UART_PRO_DMA_EN
        USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
        //
        //开启串口DMA传输
        //
        USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);
        USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);
        #endif
}[/mw_shl_code]
五、这是硬件层的事情了,搞完就到协议层了,按照文档中的格式和流程进行编写就可以了,本工程实现了以下函数列表中部分验证
[mw_shl_code=applescript,true]MCU_Status ST_UART_PRO_Synchronous_Target_MCU(void);  //同步目标MCU
MCU_Status ST_UART_PRO_Get_ID(u8 * PID);  //获取目标MCU的ID序列号
MCU_Status ST_UART_PRO_Get_Version_Protection_Status(u8 *Version);  //获取目标MCU自枚举版本和Flash的读保护状态
MCU_Status ST_UART_PRO_Get_Command(u8 *Commandlist, u8 *Versiontemp, u8 *len);  //获取目标MCU自枚举支持的命令

MCU_Status ST_UART_PRO_Read_Memory(u32 Address, u8 *pBuffer, u16 NumToRead);  //读取存储器数据
MCU_Status ST_UART_PRO_Write_Memory(u32 T_Address, u8 *pBuffer, u16 NumToWrite);  //写入存储器数据

MCU_Status ST_UART_PRO_Go_APP(u32 T_Address);  //跳转到程序区

MCU_Status ST_UART_PRO_Write_Unprotect(void);  //关闭写保护功能
MCU_Status ST_UART_PRO_Write_Protect(u8 *Sector_List, u32 Size);  //开启写保护功能
MCU_Status ST_UART_PRO_Read_Protect(void);  //开启读取保护功能
MCU_Status ST_UART_PRO_Read_Unprotect(void);  //关闭读取保护功能

MCU_Status ST_UART_PRO_Erase(u8 Page_NumToErase);  //擦除
MCU_Status ST_UART_PRO_E_Erase(u32 Address, u8 Spec_add_Flag, u8 Page);  //擦除[/mw_shl_code]
六、编写自测程序进行刷机测试
[mw_shl_code=applescript,true]ST_UART_PRO_Init(115200);  //初始化后打开副U电源
        GPIOB->BSRR = GPIO_BSRR_BS_15;
        delay_ms(500);
        
        printf("buf address 0x%x 0x%x ", &ST_UART_PRO_TX_BUF, DMA1_Channel2->CMAR);
        
        ST_UART_PRO_ISP_Mode();  //副U进入ISP模式
        if (ST_UART_PRO_Synchronous_Target_MCU() == Ready)
        {
                printf("Sub mcu Synchronous ok ");
        }
        else
        {
                printf("Sub mcu Synchronous error ");
        }
        //
        //获取目标MCU支持命令
        //
        if (ST_UART_PRO_Get_Command(com_list, &version, &com_len) == Ready)
        {
                printf("Sub mcu get command success ");
                //
                //打印目标MCU自枚举程序版本
                //
                printf("Bootloader Version V%d.%d ", (char)((version>>4) & 0x0F), (char)(version & 0x0F));
                //
                //打印支持命令
                //
                for (i = 0;i < com_len;i++)
                {
                        printf("Bootloader Command%02d = 0x%x ", i, com_list);
                }
                i = 0;
        }
        else
        {
                printf("Sub mcu get command error ");
        }
        
        //
        //去除写保护
        //
        if (ST_UART_PRO_Write_Unprotect() != Ready)
        {
                printf("Write unprotect failed ");
        }
        else
        {
                printf("Write unprotect success ");
                ST_UART_PRO_ISP_Mode();
                if (ST_UART_PRO_Synchronous_Target_MCU() == Ready)
                {
                        printf("Sub mcu Synchronous ok too ");
                }
                else
                {
                        printf("Sub mcu Synchronous error too ");
                }
        }
        
        //
        //自测试
        //
        if (ST_UART_PRO_Erase(0xFF) != Ready)
        {
                printf("Erase failed ");
        }
        else
        {
                printf("Erase success ");
        }
        file_size = test_size;
        printf("Test size %d ", file_size);
        i = 0;
        w_address = 0x08000000;
        while (file_size != 0)
        {
                i++;
                delay_ms(10);
                if (i > 200)
                {
                        i = 0;
                        HAL_LED0 ^= 1;
                }
               
                if (file_size >= 1024)
                {
                        //读取数据
                        memcpy((char *)w_buf, (const char *)&test[w_len], 1024);
                        
                        //写入flash
                        if (ST_UART_PRO_Write_Memory(w_address, w_buf, 1024) != Ready)
                        {
                                err = 1;
                        }
                        else
                        {
                                //读取数据回来校验
                                if (ST_UART_PRO_Read_Memory(w_address, r_buf, 1024) != Ready)
                                {
                                        err = 2;
                                }
                                else
                                {
                                        //比较数据
                                        if (memcmp((const char *)w_buf, (const char *)r_buf, 1024) != 0)
                                        {
                                                err = 3;
                                        }
                                        else
                                        {
                                                err = 0;
                                                w_len += 1024;
                                                file_size -= 1024;
                                                w_address += 1024;
                                        }
                                }
                        }
                }
                else
                {
                        //读取数据
                        memcpy((char *)w_buf, (const char *)&test[w_len], file_size);
                        
                        //写入flash
                        if (ST_UART_PRO_Write_Memory(w_address, w_buf, file_size) != Ready)
                        {
                                err = 11;
                        }
                        else
                        {
                                //读取数据回来校验
                                if (ST_UART_PRO_Read_Memory(w_address, r_buf, file_size) != Ready)
                                {
                                        err = 12;
                                }
                                else
                                {
                                        //比较数据
                                        if (memcmp((const char *)w_buf, (const char *)r_buf, file_size) != 0)
                                        {
                                                err = 13;
                                        }
                                        else
                                        {
                                                err = 0;
                                                file_size = 0;
                                        }
                                }
                        }
                }
               
                if (err != 0)
                {
                        printf("write flash error %d ", err);
                        break;
                }
        }
        
        printf("write flash error %d %d ", err, file_size);
        
        
//        //
//        //读取数据测试
//        //
//        ST_UART_PRO_Read_Memory(0x08000000, com_list, 20);
//        for (i = 0;i < 20;i++)
//        {
//                printf("Read Data: 0x%x ", com_list);
//        }
        
        ST_UART_PRO_Run_Mode();[/mw_shl_code]

      每发送1024字节数据,完成后再读取1024字节数据回来,和发送的数做对比,一样就算通过,否则就算失败,失败后没有重新刷,方便读取出错位置,实际应用中,大家自行增加响应的失败处理方式
七、附上一个UART抓的波形
3.jpg
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。