天天看点

[单片机框架][DFU] Dfu升级例子 带crc校验+超时机制+led指示灯+芯片加锁+芯片自擦

/********************************************************************************
* @file    main.c
* @author  jianqiang.xue
* @Version V1.0.0
* @Date    2021-04-03
* @brief   NULL
********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include "RTE_Components.h"
#include CMSIS_device_header

#include "bsp_system_clock.h"
#include "bsp_gpio.h"
#include "bsp_uart.h"
#include "bsp_flash.h"
#include "sys_api.h"

#include "crc16.h"
#include "check_uid.h"
#include "errorno.h"
#include "str_hex.h"
#include "x_strtok.h"
#include "kv_sys.h"
/* Private Includes ----------------------------------------------------------*/
#include "business_gpio.h"
#include "business_function.h"
#include "dfu.h"
#include "main.h"
/* Private Define ------------------------------------------------------------*/
#define LOG(...)           \
    do                     \
    {                      \
        bsp_uart_send_nbyte(BSP_UART_0, NULL, sprintf((char *)bsp_uart_get_txbuff(BSP_UART_0), __VA_ARGS__)); \
    } while (0U)

/* Private Typedef -----------------------------------------------------------*/
/* Private Define ------------------------------------------------------------*/
#define CRC_LEN                       2
#define PACK_HEAD_LEN                 2
#define CRC16_INIT_VAL                0xFFFF

/* Private Macro -------------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
uint32_t g_write_flash_addr   = 0;
// DFU 超时机制 15s没有数据达到,则判断app是否存在,如果存在则跳转至app
static dfu_file_info_t dfu_file_info = {0};
uint32_t g_dfu_tickstart      = 0;
uint16_t g_dfu_timeout_ms     = 15000;
bool g_dfu_timeout_flag       = 0; // 0 -- 未检测  1--检测完成

/* Private Function Prototypes -----------------------------------------------*/

static void dfu_file_info_data(uint8_t *data, uint16_t len)
{
    char *token_r;
    char *argv[2];
    char *arg;
    uint8_t argc    = 0;
    uint8_t crc[2]  = {0, 0};
    uint8_t crc_len = 0;
    uint16_t crc16  = 0;
    // LOG("data:%s, len:%d\r\n", data, len);
    arg = x_strtok_s((char*)data, ",", &token_r);
    while (arg != NULL)
    {
        argv[argc++] = arg;
        arg = x_strtok_s(NULL, ",", &token_r);
    }

    if (argc == 2)
    {
        crc_len = str_to_hex(argv[0], crc);
        if (crc_len == CRC_LEN)
        {
            crc16 = (crc[0] << 8) | crc[1];
        }
        else
        {
            goto end;
        }

        memset(&dfu_file_info, 0, sizeof(dfu_file_info_t));
        dfu_file_info.file_crc16 = crc16;
        dfu_file_info.file_size = atoi(argv[1]);
        // LOG("UP^OK,0,crc:%x, size:%d\r\n", crc16, atoi(argv[1]));
        LOG("UP^OK,0,%d\r\n", BS_UART0_CACHE_SIZE);
    }
    else
    {
end:
        LOG("UP^FAIL,%d\r\n", E_INVAL_PARM);
    }
}

static void dfu_file_data(uint8_t *data, uint16_t len)
{
    uint16_t rx_crc;
    uint16_t cal_crc;
    uint8_t  pack_id;
    uint8_t  pack_len;
    uint8_t *pack_data;
    uint8_t  pdu_len;

    if (len < PACK_HEAD_LEN + CRC_LEN)
    {
        //LOG("UP^FAIL,%d\r\n", E_INVAL_LEN);
        LOG("UP^FAIL,LEN\r\n");
        return;
    }

    pack_len = *(data + 1);
    pdu_len = pack_len + PACK_HEAD_LEN;

    if (len != pdu_len + CRC_LEN)
    {
        //LOG("UP^FAIL,%d,%d,%d\r\n", E_INVAL_DATA, pdu_len + CRC_LEN, len);
        LOG("UP^FAIL,LEN1\r\n");
        return;
    }

    pack_id   = *(data + 0);
    pack_data = (data + 2);

    // pack的最后两个字节为crc值, 这里的len是按1计算的,所以按数组时,要多减1.
    rx_crc = (*(data + len - 2) << 8) | *(data + len - 1);
    cal_crc = crc16(CRC16_INIT_VAL, pack_data, pack_len);
    // LOG("CRC,rx:%x,cal:%x\r\n",rx_crc, cal_crc);
    if (rx_crc != cal_crc)
    {
        //LOG("UP^FAIL,%d,R:%x,C:%x,L:%d\r\n", E_CRC, rx_crc, cal_crc, pack_len);
        LOG("UP^FAIL,CRC1\r\n");
        return;
    }

    // 第一次接到数据,则擦除flash
    if (dfu_file_info.flash_flag == false && dfu_file_info.file_size != 0)
    {
        dfu_file_info.flash_flag = true;
        // 如果固件大小 < APP容量的一半,则使用备份升级
        if (dfu_file_info.file_size < (BS_FLASH_APP_SIZE / 2))
        {
            g_write_flash_addr = BS_FLASH_OTA_ADDR;
            sys_disable_irq();
            bsp_flash_erase_page(g_write_flash_addr, ((BS_FLASH_APP_SIZE / 2) / BS_FLASH_PAGE_SIZE) + 1);
            sys_enable_irq();
        }
        else
        {
            g_write_flash_addr = BS_FLASH_APP_ADDR;
            sys_disable_irq();
            bsp_flash_erase_page(g_write_flash_addr, (BS_FLASH_APP_SIZE / BS_FLASH_PAGE_SIZE) + 1);
            sys_enable_irq();
        }
        return;
    }

    if ((pack_id == dfu_file_info.old_pack_id) ||
        (pack_id < dfu_file_info.old_pack_id) ||
        (pack_id > dfu_file_info.old_pack_id && (pack_id - dfu_file_info.old_pack_id != 1)))
    {
        LOG("UP^FAIL,%d,%d,%d\r\n", E_MSG, dfu_file_info.old_pack_id, pack_id);
        return;
    }

    dfu_file_info.old_pack_id = pack_id;

    if (dfu_file_info.flash_flag == true && dfu_file_info.file_size != 0)
    {
        if (g_write_flash_addr == 0)
        {
            return;
        }
        bsp_flash_write_nbyte_s(g_write_flash_addr + dfu_file_info.current_size, pack_data, pack_len);
        dfu_file_info.current_size += pack_len;
        LOG("UP^OK,%d\r\n", pack_id);
    }
}

static void dfu_check_file(uint8_t *data, uint16_t len)
{
    if (dfu_file_info.flash_flag == false || dfu_file_info.file_size == 0 ||
        dfu_file_info.file_size != dfu_file_info.current_size || g_write_flash_addr == 0)
    {
        LOG("UP^FAIL,FILE\r\n");
        return;
    }

    uint16_t cal_crc;
    cal_crc = crc16(CRC16_INIT_VAL, (uint8_t *)g_write_flash_addr, dfu_file_info.current_size);
    if (cal_crc != dfu_file_info.file_crc16)
    {
        // LOG("UP^FAIL,%x %x\r\n", cal_crc, dfu_file_info.file_crc16);
        LOG("UP^FAIL,CRC\r\n");
    }
    else
    {
        LOG("UP^OK,UP\r\n");
        g_boot_info.app_crc         = dfu_file_info.file_crc16;
        g_boot_info.boot_carry_size = dfu_file_info.current_size;
        if (g_write_flash_addr == BS_FLASH_OTA_ADDR)
        {
            g_boot_info.boot_state      = BOOT_STATE_MOVE_OTA_IN_APP;
            kv_set_env(BS_KV_KEY_BOOT_INFO, (uint8_t *)&g_boot_info, sizeof(boot_info_t));
            delay_ms(5);
        }
        else if (g_boot_info.boot_state < 2)
        {
            g_boot_info.boot_state      = BOOT_STATE_RUN_APP;
            kv_set_env(BS_KV_KEY_BOOT_INFO, (uint8_t *)&g_boot_info, sizeof(boot_info_t));
            delay_ms(5);
        }
        // 跳转启动
        sys_reset();
    }
}

static void dfu_data_analysis(uint8_t *data, uint16_t len)
{
    data[len] = '\0';
    if (strncmp((const char *)data, "FILE_INFO=", strlen("FILE_INFO=")) == 0)
    {
        dfu_file_info_data((data + strlen("FILE_INFO=")), len - strlen("FILE_INFO="));
    }
    else if (strncmp((const char *)data, "CHECK_FILE=", strlen("CHECK_FILE=")) == 0)
    {
        dfu_check_file((data + strlen("CHECK_FILE=")), len - strlen("CHECK_FILE="));
    }
}

static void atcmd_chip_lock(uint8_t *data, uint16_t len)
{
    uint16_t uid_crc  = 0;
    if (len < 2)
    {
        return;
    }
    // 得到上位机发送的校验值
    uid_crc = (*data) << 8 | (*(data + 1));

    g_boot_info.boot_state = BOOT_STATE_RUN_APP;
    g_boot_info.chip_lock  = uid_crc;            // 填写特殊码解锁烧录
    kv_set_env(BS_KV_KEY_BOOT_INFO, (uint8_t *)&g_boot_info, sizeof(boot_info_t));
    // 判断上位机的校验值于自身校验值是否匹配
    if (g_cal_crc == uid_crc)
    {
        LOG("AT^OK,UNLOCK\r\n");
        delay_ms(5);
        sys_reset();
    }
    else
    {
#if !DEBUG_MODE
       // 关闭SWD功能 配置PC7和PD1的端子功能模式  周边模块功能模式使能
       RCC->UNLOCK  = 0x55AA6699;
       RCC->SWDIOCR = (0x5A69 << RCC_SWDIOCR_KEY_Pos);
       RCC->UNLOCK  = 0x55AA6698;
       LOG("AT^OK,LOCK\r\n");
#else
       LOG("AT^OK,LOCK,%04x|%04x\r\n", g_cal_crc, uid_crc);
#endif
    }
}

static void uart0_rx_callback(void)
{
    uint8_t temp;
    uint8_t *buff = bsp_uart_get_rxbuff(BSP_UART_0);
    uint16_t len = bsp_uart_get_rxbuff_position(BSP_UART_0);
    bool flag = 0;
    if (len >= (BS_UART0_CACHE_SIZE - 1))
    {
        bsp_uart_reset_rxbuff(BSP_UART_0);
        return;
    }
    if (len >= 10 && strncmp((const char *)buff, "UP^DATA=", strlen("UP^DATA=")) == 0)
    {
        temp = strlen("UP^DATA=") + PACK_HEAD_LEN + CRC_LEN;
        if (len - temp - 1 == *(buff + strlen("UP^DATA=") + 1))
        {
            dfu_file_data((buff + strlen("UP^DATA=")), len - strlen("UP^DATA=") - 1);
            flag = true;
            g_dfu_tickstart = HAL_GetTick();
        }
    }
    else if (len >= 10 && strncmp((const char *)buff, "AT^LOCK=", strlen("AT^LOCK=")) == 0)
    {
        atcmd_chip_lock((buff + strlen("AT^LOCK=")), 2);
        flag = true;
    }
    else if (len >= 11 && strncmp((const char *)buff, "AT^XJQ=1995", strlen("AT^XJQ=1995")) == 0)
    {
        bsp_flash_erase_page(BS_FLASH_APP_ADDR, (BS_FLASH_APP_SIZE / BS_FLASH_PAGE_SIZE) + 1);
        bsp_flash_erase_page(BS_KV_BASE_ADDR, BS_FLASH_KV_PAGE);
        bsp_flash_erase_page(BS_FLASH_START_ADDR + BS_FLASH_PAGE_SIZE, (BS_FLASH_BOOT_SIZE / BS_FLASH_PAGE_SIZE) + 1);
    }
    else if (len >= 10 && strncmp((const char *)buff, "bootloader", strlen("bootloader")) == 0)
    {
        LOG("UP^OK,dfu mode\r\n");
        flag = true;
    }
    else if (len >= 2 && buff[len - 1] == '\n' && buff[len - 2] == '\r')
    {
        if (strncmp((const char *)buff, "UP^", strlen("UP^")) == 0)
        {
            dfu_data_analysis((buff + strlen("UP^")), len - strlen("UP^"));
        }
        flag = true;
    }
    else
    {
        if (len >= (BS_UART0_CACHE_SIZE - 1))
        {
            flag = true;
        }
    }
    if (flag)
    {
        bsp_uart_reset_rxbuff(BSP_UART_0);
    }
}

static void uart0_rx_full_callback(void)
{
    memset(bsp_uart_get_rxbuff(BSP_UART_0), 0, BS_UART0_CACHE_SIZE);
    bsp_uart_reset_rxbuff(BSP_UART_0);
}

/* Public Function Prototypes -----------------------------------------------*/
void dfu_main(void)
{
    uint32_t dfu_tick_current = 0;
    uint32_t tick_led         = 0;
    bool key_flag             = false;
    uint8_t key_tick          = false;
    bool erase_flag           = false;

    bsp_uart_init(BSP_UART_0);
    LOG("UP^OK,dfu mode,%04x\r\n", g_uid_sum);
    bsp_uart_rx_irq_callback(BSP_UART_0, uart0_rx_full_callback);
    memset(&dfu_file_info, 0, sizeof(dfu_file_info_t));
    g_dfu_tickstart = HAL_GetTick();
    while (1)
    {
        // 超时机制
        if ((g_boot_info.boot_state == BOOT_STATE_IN_DFU) && (!g_dfu_timeout_flag))
        {
            dfu_tick_current = HAL_GetTick();
            if (dfu_tick_current - g_dfu_tickstart > g_dfu_timeout_ms)
            {
                g_dfu_timeout_flag = true;
                sys_jump_app();
            }
        }
        tick_led ++;
        if(tick_led % 0xFFF == 0)
        {
            // boot 模式 指示灯闪烁
            bsp_gpio_set_toggle(BS_LED0_GPIO_PORT, BS_LED0_PIN);
        }
        if (!g_dfu_timeout_flag)
        {
            // boot 模式进行自毁工作
            if (erase_flag)
            {
                bsp_flash_erase_page(BS_FLASH_APP_ADDR, (BS_FLASH_APP_SIZE / BS_FLASH_PAGE_SIZE) + 1);
                bsp_flash_erase_page(BS_KV_BASE_ADDR, BS_FLASH_KV_PAGE);
                sys_reset();
            }
            if (bsp_gpio_get_state(BOARD_BUTTON_SYS_PORT, BOARD_BUTTON_SYS_PIN) == BOARD_BUTTON_SYS_PRESS_LEVEL)
            {
                if (!key_flag)
                {
                    continue;
                }
                else
                {
                    key_tick ++;
                    key_flag = false;
                    if (key_tick > 9)
                    {
                        key_tick = 0;
                        erase_flag = true;
                    }
                }
            }
            else
            {
                if (!key_flag)
                {
                    key_flag = true;
                }
            }
        }
        uart0_rx_callback();
    }
}      
/********************************************************************************
* @file    dfu.h
* @author  jianqiang.xue
* @version V1.0.0
* @date    2021-04-09
* @brief   NULL
********************************************************************************/

#ifndef __DFU_H
#define __DFU_H

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>

typedef struct
{
    uint16_t file_crc16;
    uint32_t file_size;
    uint32_t current_size;
    uint8_t  old_pack_id;
    bool  flash_flag;
} dfu_file_info_t;

/* Public Function Prototypes ------------------------------------------------*/
void dfu_main(void);
#endif      

继续阅读