stm32cubemx IAP升级(三)

stm32cubemx IAP升级- UART+DMA实现不定长收发数据

板卡:Nucleo-L412
平台:macbook pro
工具:vscode stm32cubemx stm32cubeProgramer cmake toolchain

Stm32CubeMx的配置

选择开启一路串口并配置成DMA,并使能中断,配置中断优先级。
stm32cubemx IAP升级(三)_第1张图片
stm32cubemx IAP升级(三)_第2张图片
stm32cubemx IAP升级(三)_第3张图片## 代码实现
main.c
1、定义全局接收数组
uint8_t UART2_RX_BUF[UART2_RX_SIZE] = {0}; //串口接收缓存
uint8_t UART2_RX_LEN;
2、使能空闲中断,以及DMA接收数据
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart2,UART2_RX_BUF,UART2_RX_SIZE); //启动DMA接收,UART1_RX_BUF:数据接收缓冲
3、修改stm32l4xx_it.c,将USART2_IRQHandler函数修改如下

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != RESET))
  {
   	 	__HAL_UART_CLEAR_IDLEFLAG(&huart2);                                     //清除空闲状态标志
    	HAL_UART_DMAStop(&huart2);                                              //关闭DMA传输

	    
 		UART2_RX_LEN = UART2_RX_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); 	//计算接收到的数据长度
		USAR_UART_IDLECallback(&huart2,UART2_RX_LEN );                          //调用回调函数
	}
 /* USER CODE END USART2_IRQn 1 */
}

USAR_UART_IDLECallback函数定义,UART2_RX_BUF即收到的数据,rxlen即为收到数据的长度

void USAR_UART_IDLECallback(UART_HandleTypeDef *huart,uint8_t rxlen )
{
	if(huart == &huart2)                                        //判断是否为串口1产生中断
	{
    if (rxlen >0)
      handle_uart_message(UART2_RX_BUF,rxlen);
		// HAL_UART_Transmit_DMA(&huart2, UART2_RX_BUF,rxlen);    //将接收到的不定长数据发送到上位机
	  rxlen = 0;                                                //清除数据长度计数
    HAL_UART_Receive_DMA(&huart2,UART2_RX_BUF,UART2_RX_SIZE); //重新打开DMA接收	        
	}
}

handle_uart_message函数为收到升级命令,数据,以及升级完成的命令的解析函数。
串口升级协议如下:
cmd + data_lenght + data0 + …+ datax + checksum
1、获取版本号 0x01 0x02 0x00 0x00 checksum
2、升级
1、进入升级模式 0x02 0x02 0x00 0x00 checksum
2、升级文件大小 0x03 0x04 0x00 0x00 0x00 0x00 checksum
3、数据包发送 0x04 0x80 0x00 0x00 0x00 0x00 … checksum
4、数据包发送完成 0x05 0x02 0x00 0x00 checksum

typedef enum uart_update_cmd
{
    UART_GET_SYSTEM_VERSION_CMD                 = 0x01,
    UART_ENTER_SYSTEM_UPDATE_MODE_CMD           = 0x02,
    UART_GET_SYSTEM_FILE_SIZE_CMD               = 0x03,
    UART_RECEIVE_SYSTEM_UPDATE_CMD              = 0x04,
    UART_COMPLETE_SYSTEM_UPDATE_CMD             = 0x05,

}MI_UartUpdateCmd;

MI_BOOL handle_uart_message(MI_U8 *p_buff,MI_U32 len)
{
    switch (p_buff[UART_CMD_INDEX])
    {
    case UART_GET_SYSTEM_VERSION_CMD/* constant-expression */:
        /* 获取系统版本号 */
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            system_info_get_system_version(version);
            HAL_UART_Transmit_DMA(&huart2, version,strlen((const char *)version));       //将接收到的不定长数据发送到上位机
        }
        break;
    case UART_ENTER_SYSTEM_UPDATE_MODE_CMD:
        /* 进入升级模式命令 指示灯变为100ms闪烁一次*/
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            led_freq = LED_TOGGLE_100_MS;
            w_time = 0;
            packets_numer = 0;
            upgrade_file_size = 0;
            remain_packets_numer = 0;
            isRunningUpdate = 1;
            printf("Mcu Receive Update Command and Wait Receive Data Packets \r\n");
            printf("Now to Erase Download Pages \r\n");
            printf("\r\n");
            int ret = stm32_erase_flash(DOWNLOAD_START_SECTOR_ADDR,DOWNLOAD_END_SECTOR_ADDR);

            printf("\r\n");

        }
        break;
    case UART_GET_SYSTEM_FILE_SIZE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            int a = (int)p_buff[2];
            int b = (int)p_buff[3];
            int c = (int)p_buff[4];
            int d = (int)p_buff[5];
            upgrade_file_size = (a<<24) | (b<<16) | (c<<8) | (d);

            if (upgrade_file_size % UPGRADE_DATA_PACKAGES_LENGHT == 0)  //如果整除128 
            {
                packets_numer = upgrade_file_size/128;
            }
            else
            {
                packets_numer = ((upgrade_file_size/128) + 1);
            }
            printf("End Erase Download Pages Down\r\n");
            printf("\r\n");

            printf("receive upgrade file size %d\r\n",upgrade_file_size);
            printf("data packets number  %d\r\n",packets_numer);
            printf("\r\n");
        }
        break;
    case UART_RECEIVE_SYSTEM_UPDATE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            printf("receive packets........................%03d [100] \r\n",(((w_time + 1) * 100) / packets_numer));
            memset(w_buff,0,sizeof(w_buff));
            memcpy(w_buff,&p_buff[2],sizeof(w_buff));

            stm32_flash_write(DOWNLOAD_START_SECTOR_ADDR+(w_time * UPGRADE_DATA_PACKAGES_LENGHT),w_buff,sizeof(w_buff));

            if (w_time + 1 == packets_numer)
            {
                led_freq = LED_TOGGLE_1000_MS;
                printf("receive packets........................ done!\r\n");
            }
            w_time ++;
        }
        break;
    case UART_COMPLETE_SYSTEM_UPDATE_CMD:
        if (calculate_checksum_and_length(p_buff,len) == MI_TRUE)
        {
            system_info_set_update_flag(SYSTEM_UPDATE);
            printf("Now Reboot !\r\n");
            app_soft_reset();
        }
        break;    
    default:
        break;
    }
    return MI_TRUE;
}

校验采用CRC16

static MI_U16 CRC16(MI_U8 * buf, MI_U16 len)
{
	MI_U16 i;
	MI_U16 crc = 0xffff;
 
	if (len == 0) {
		len = 1;
	}
	while (len--) {
        
		crc ^= *buf;
		for (i = 0; i<8; i++) 
		{            
			if (crc & 1) {               
				crc >>= 1;        
				crc ^= 0xA001;            
			}      
			else {               
				crc >>= 1;            
			}       
		}     
		buf++;
	}
	return(crc);
}

static MI_BOOL calculate_checksum_and_length(MI_U8 * buf, MI_U16 len)
{
    MI_U16 crc16 = CRC16(buf,len-2);
    if ((crc16 & 0x00ff) == buf[len-2] && ((crc16 >> 8) & 0x00ff) == buf[len-1])
    {
        if (buf[UART_CMD_LENGTH] == (len-2-2))
        {
            return MI_TRUE;
        }
        else
        {
            printf("crc16 right ,but cmd error\r\n"); 
            //_Error_Handler(__FUNCTION__,__LINE__);
            return MI_FALSE; 
        }
    }
    else
    {
        printf("crc16 error and right crc16 = 0x%04x\r\n",crc16);  
        //_Error_Handler(__FUNCTION__,__LINE__);
        return MI_FALSE; 
    }
}

flash写函数:

/* FLASH大小:STM32L412 :128K */
#define STM32_FLASH_SIZE         0x00020000UL
/* FLASH起始地址 */
#define STM32_FLASH_BASE         0x08000000UL
/* FLASH结束地址 */
#define STM32_FLASH_END          (STM32_FLASH_BASE | STM32_FLASH_SIZE)
/* FLASH页大小:2K */
#define STM32_FLASH_PAGE_SIZE    FLASH_PAGE_SIZE
/* FLASH总页数  64 页*/
#define STM32_FLASH_PAGE_NUM     (STM32_FLASH_SIZE / STM32_FLASH_PAGE_SIZE)


//针对STM32L412单片机,通过地址获取所在页的函数如下
MI_U32 get_flash_page(MI_U32 address)											//获取地址所在的
{
    MI_U32 page = 0;
    if (address < (STM32_FLASH_BASE + FLASH_BANK_SIZE))
        page = (address - STM32_FLASH_BASE) / FLASH_PAGE_SIZE;
    else
        page = (address - (STM32_FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;
    return page;
}

MI_U8 stm32_erase_flash(MI_U32 start_addr,MI_U32 end_addr)		//擦除flash
{
    FLASH_EraseInitTypeDef EraseInitStruct;
    MI_U32 FirstPages = 0,LastPages = 0, NbPages = 0;
    uint32_t pageError = 0;
    HAL_FLASH_Unlock();                                         //首先解锁flash
    FirstPages = get_flash_page(start_addr);		            //获取要擦除的第一个页
    LastPages = get_flash_page(end_addr);
    NbPages = LastPages - FirstPages;	                      //获取擦除的页数量

    /* Fill EraseInit structure*/
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;	//页擦除
    EraseInitStruct.Banks = FLASH_BANK_1;
    EraseInitStruct.Page = FirstPages;
    EraseInitStruct.NbPages = NbPages;
    if(HAL_FLASHEx_Erase(&EraseInitStruct, &pageError) != HAL_OK)
    { 
          HAL_FLASH_Lock();                                       //上锁
      return 2;												//擦除有错误,返回2
    }
      //下面这些是清除标志位
    __HAL_FLASH_DATA_CACHE_DISABLE();
    __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
  
    __HAL_FLASH_DATA_CACHE_RESET();
    __HAL_FLASH_INSTRUCTION_CACHE_RESET();
  
    __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
    __HAL_FLASH_DATA_CACHE_ENABLE();
  
    HAL_FLASH_Lock();                                           //上锁
    return 0;
}


MI_BOOL stm32_flash_write(MI_U32 dest_addr, MI_U8 *src, MI_U32 Len)
{
    MI_U32 i = 0;

    HAL_FLASH_Unlock();
    /* Clear OPTVERR bit set on virgin samples */
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);

    for(i = 0; i < Len; i += 8)
    {
      /* Device voltage range supposed to be [2.7V to 3.6V], the operation will
        be done by byte */
      if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (MI_U32)(dest_addr+i), *(uint64_t*)(src+i)) == HAL_OK)
      {
      /* Check the written value */
        if(*(uint64_t *)(src + i) != *(uint64_t*)(dest_addr+i))
        {
          /* Flash content doesn't match SRAM content */
          HAL_FLASH_Lock();            
          return -1;
        }

      }
      else
      {
          HAL_FLASH_Lock();            

        /* Error occurred while writing data in Flash memory */
        return -1;
      }
    }
    HAL_FLASH_Lock();            

    return 0;
}

针对L412单片机,写入时是按FLASH_TYPEPROGRAM_DOUBLEWORD格式写入的,所以要8个地址++,这点要注意。

你可能感兴趣的:(stm32,Iap升级,stm32,单片机,嵌入式硬件)