/****************************************************************************** * file Src/common.c * author YuLiang * version 1.0.0 * date 10-Jun-2022 * brief This file provides all the common functions. ****************************************************************************** * @attention * *

© COPYRIGHT(c) 2022 LandPower

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #include #include #include #include "main.h" #include "menu.h" #include "common.h" #include "flash_if.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* 中断中打印限制长度. */ #define USART_PRINT_BUF_LEN 128 /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ dev_info_t dev_info; dev_record_t dev_record; uint8_t number_str[NUM_LEN]; static uint32_t crc32Table[256]; static uint32_t dev_magic; static uint32_t dev_magic_bak; extern uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE]; /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /** * @brief Convert an Integer to a string * @param p_str: The string output pointer * @param intnum: The integer to be converted * @retval None */ void Int2Str(uint8_t *p_str, uint32_t intnum) { uint32_t i, divider = 1000000000, pos = 0, status = 0; for (i = 0; i < 10; i++) { p_str[pos++] = (intnum / divider) + 48; intnum = intnum % divider; divider /= 10; if ((p_str[pos-1] == '0') & (status == 0)) { pos = 0; } else { status++; } } p_str[pos] = 0; } /** * @brief Convert a string to an integer * @param p_inputstr: The string to be converted * @param p_intnum: The integer value * @retval 1: Correct * 0: Error */ uint32_t Str2Int(uint8_t *p_inputstr, uint32_t *p_intnum) { uint32_t i = 0, res = 0; uint32_t val = 0; if ((p_inputstr[0] == '0') && ((p_inputstr[1] == 'x') || (p_inputstr[1] == 'X'))) { i = 2; while ( ( i < 11 ) && ( p_inputstr[i] != '\0' ) ) { if (ISVALIDHEX(p_inputstr[i])) { val = (val << 4) + CONVERTHEX(p_inputstr[i]); } else { /* Return 0, Invalid input */ res = 0; break; } i++; } /* valid result */ if (p_inputstr[i] == '\0') { *p_intnum = val; res = 1; } } else /* max 10-digit decimal input */ { while ( ( i < 11 ) && ( res != 1 ) ) { if (p_inputstr[i] == '\0') { *p_intnum = val; /* return 1 */ res = 1; } else if (((p_inputstr[i] == 'k') || (p_inputstr[i] == 'K')) && (i > 0)) { val = val << 10; *p_intnum = val; res = 1; } else if (((p_inputstr[i] == 'm') || (p_inputstr[i] == 'M')) && (i > 0)) { val = val << 20; *p_intnum = val; res = 1; } else if (ISVALIDDEC(p_inputstr[i])) { val = val * 10 + CONVERTDEC(p_inputstr[i]); } else { /* return 0, Invalid input */ res = 0; break; } i++; } } return res; } /** * @brief Print a string on the HyperTerminal * @param p_string: The string to be printed * @retval None */ void Serial_PutString(uint8_t *p_string) { uint16_t length = 0; while (p_string[length] != '\0') { length++; } HAL_UART_Transmit(UartHandle, p_string, length, TX_TIMEOUT); } /** * @brief Transmit a byte to the HyperTerminal * @param param The byte to be sent * @retval HAL_StatusTypeDef HAL_OK if OK */ HAL_StatusTypeDef Serial_PutByte( uint8_t param ) { HAL_StatusTypeDef rv = HAL_ERROR; /* May be timeouted... */ if ( UartHandle->gState == HAL_UART_STATE_TIMEOUT ) { UartHandle->gState = HAL_UART_STATE_READY; } rv = HAL_UART_Transmit(UartHandle, ¶m, 1, TX_TIMEOUT); return rv; } /** * @brief Transmit a bytes to the HyperTerminal * @param param The byte to be sent * @retval HAL_StatusTypeDef HAL_OK if OK */ HAL_StatusTypeDef Serial_PutBytes(uint8_t* buf, uint16_t len, uint32_t timeout) { HAL_StatusTypeDef rv = HAL_ERROR; /* May be timeouted... */ if ( UartHandle->gState == HAL_UART_STATE_TIMEOUT ) { UartHandle->gState = HAL_UART_STATE_READY; } rv = HAL_UART_Transmit(UartHandle, buf, len, timeout); return rv; } /* 通过 usart1 输出格式化字符. 原地等待发送, 与操作系统无关, * 可以在中断中使用, 尽量不要在中断中使用, 太耗时间. */ void vty_print(char *format, ...) { static char buf[USART_PRINT_BUF_LEN]; va_list ap; va_start(ap, format); while(HAL_UART_STATE_BUSY_TX == UartHandle->gState) continue; if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0) { HAL_UART_Transmit(UartHandle, (uint8_t*)buf, strlen(buf), 1000); } va_end(ap); } /* 计算crc32 table值. */ static uint32_t _crc32_reflect( uint32_t value, uint8_t bits ) { uint32_t reflection = 0; for(uint8_t i = 0; i < bits ; ++i) { if (value & (1u << i )) reflection |= 1 << (bits - 1 - i); } return reflection; } /** * @brief 初始化crc32表. * @param none * @retval none */ void crc32_table_init(void) { uint32_t int1 = 1; uint32_t const value_high_bit = int1 << 31u; uint8_t dividend = 0; do { uint32_t remainder = 0; for(uint8_t mask = 1u << (8 - 1u); mask; mask >>= 1) { if (dividend & mask) remainder ^= value_high_bit; if (remainder & value_high_bit) { remainder <<= 1; remainder ^= 0x04C11DB7u; } else remainder <<= 1; } crc32Table[_crc32_reflect(dividend, 8)] = _crc32_reflect(remainder, 32); } while(++dividend); } /** * @brief 计算crc32值. * @param buf: 数据. * @param len: 数据长度. * @retval crc32值. */ uint32_t crc32_update(uint32_t crc, char* buf, uint32_t len) { uint32_t i = 0; for(i = 0; i < len; i++) crc = crc32Table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); return crc; } /* 读取config主份存储区. */ static HAL_StatusTypeDef _dev_info_read(void) { uint8_t i = 0; HAL_StatusTypeDef rv = HAL_ERROR; memset(&dev_info, 0, sizeof(dev_info_t)); /* 读取主存储区. */ for(i = 0; i < DEV_FLASH_REPETITION; i++) { rv = spi_flash_read(INFO_ADDRESS, (uint8_t*)(&dev_info), sizeof(dev_info_t)); if (HAL_OK == rv) break; HAL_Delay(5000); } return rv; } /* 读取info备份存储区. */ static HAL_StatusTypeDef _dev_info_read_bak(void) { uint8_t i = 0; HAL_StatusTypeDef rv = HAL_ERROR; memset(&dev_info, 0, sizeof(dev_info_t)); /* 读取备存储区. */ for(i = 0; i < DEV_FLASH_REPETITION; i++) { rv = spi_flash_read(INFO_ADDRESS_BAK, (uint8_t*)(&dev_info), sizeof(dev_info_t)); if (HAL_OK == rv) break; HAL_Delay(5000); } if (HAL_OK == rv) { dev_magic = dev_info.magic; dev_magic_bak = dev_info.magic_bak; } return rv; } /* 保存信息文件. */ static HAL_StatusTypeDef _dev_info_save(uint32_t addr) { uint8_t i = 0; HAL_StatusTypeDef rv = HAL_OK; /* 存多次失败才算失败. */ for(i = 0; i < DEV_FLASH_REPETITION; i++) { rv = spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE); rv |= spi_flash_write(addr, (uint8_t*)(&dev_info), sizeof(dev_info_t)); if (HAL_OK == rv) break; } Serial_PutString("Info("); Int2Str(number_str, addr); Serial_PutString(number_str); Serial_PutString(") save "); Int2Str(number_str, rv); Serial_PutString(number_str); Serial_PutString(".\r\n"); return rv; } /* 恢复出厂信息. */ static HAL_StatusTypeDef _dev_info_default_set(void) { HAL_StatusTypeDef rv = HAL_OK; memset(&dev_info, 0, sizeof(dev_info_t)); dev_info.magic = INIT_DONE_MAGIC; dev_info.magic_bak = INIT_DONE_MAGIC; dev_info.type_m = 0x2; dev_info.type_s = 0x1; dev_info.mac[0] = 0x00; dev_info.mac[1] = 0x19; dev_info.mac[2] = 0x20; dev_info.mac[3] = 0x22; dev_info.mac[4] = 0x11; dev_info.mac[5] = 0x01; dev_info.id[0] = dev_info.mac[5]; dev_info.id[1] = dev_info.mac[4]; dev_info.id[2] = dev_info.mac[3]; dev_info.id[3] = dev_info.mac[2]; dev_info.ip[0] = 10; memcpy(&dev_info.ip[1], &dev_info.mac[3], 3); dev_info.ip_mask[0] = 255; dev_info.ip_gw[0] = 10; dev_info.ip_gw[3] = 1; dev_info.server_ip[0] = 111;dev_info.server_ip[1] = 47; dev_info.server_ip[2] = 21;dev_info.server_ip[3] = 141; dev_info.server_port = 8809; snprintf((char*)dev_info.boot_version, DEV_VERSION_STR_LEN, "V%d.%d.%d.%d", dev_info.type_m, dev_info.type_s, VERSION_MAJOR, VERSION_MINOR); strncpy((char*)dev_info.boot_compile_time, COMPILE_TIME, DEV_COMPILE_TIME_LEN - 1); rv = dev_info_save(); Serial_PutString("Info set default "); Int2Str(number_str, rv); Serial_PutString(number_str); Serial_PutString(".\r\n"); return rv; } /* 判断分区是否完整. */ static int32_t _dev_info_is_intact(uint32_t magic, uint32_t magic_bak) { if (magic != INIT_DONE_MAGIC && magic != UPDATE_MAGIC && magic != UPDATE_OK_MAGIC && magic != UPDATE_ERROR_MAGIC) return FALSE; if (magic_bak != INIT_DONE_MAGIC) return FALSE; return TRUE; } /** * @brief 初始化设备信息文件. * @param none * @retval HAL_xx. */ HAL_StatusTypeDef dev_info_init(void) { HAL_StatusTypeDef rv = HAL_OK; /* 检查结构体大小. */ if (sizeof(dev_info_t) != DEV_INFO_SIZE) { Serial_PutString("Info size is not 512.\r\n"); while(1) { HAL_GPIO_TogglePin(LED_RUN_GPIO_Port, LED_RUN_Pin); HAL_Delay(50); } } /* 读取失败不做处理. */ _dev_info_read_bak(); _dev_info_read(); /* 主备存储区都正常,什么都不用做. */ if (_dev_info_is_intact(dev_info.magic, dev_info.magic_bak) && _dev_info_is_intact(dev_magic, dev_magic_bak)) NULL; /* 主正常,备不正常,使用主配置覆盖备配置. */ else if(_dev_info_is_intact(dev_info.magic, dev_info.magic_bak) && !(_dev_info_is_intact(dev_magic, dev_magic_bak))) rv = _dev_info_save(INFO_ADDRESS_BAK); /* 主不正常,备正常,使用备配置覆盖主配置. */ else if(!(_dev_info_is_intact(dev_info.magic, dev_info.magic_bak)) && _dev_info_is_intact(dev_magic, dev_magic_bak)) { /* 如果读取备失败,恢复出厂配置. */ if ((rv = _dev_info_read_bak()) != HAL_OK) _dev_info_default_set(); else rv = _dev_info_save(INFO_ADDRESS); } /* 其他情况一律视为flash没有被初始化. */ else rv = _dev_info_default_set(); /* 版本发生变化,保存版本信息. */ if (IS_VERSION_CHG) { snprintf((char*)dev_info.boot_version, DEV_VERSION_STR_LEN, "V%d.%d.%d.%d", dev_info.type_m, dev_info.type_s, VERSION_MAJOR, VERSION_MINOR); strncpy((char*)dev_info.boot_compile_time, COMPILE_TIME, DEV_COMPILE_TIME_LEN - 1); rv |= dev_info_save(); } return rv; } /** * @brief 读取record主份存储区. * @param none * @retval HAL_xx. */ HAL_StatusTypeDef dev_record_read(void) { uint8_t i = 0; HAL_StatusTypeDef rv = HAL_ERROR; memset(&dev_record, 0, sizeof(dev_record_t)); /* 读取存储区. */ for(i = 0; i < DEV_FLASH_REPETITION; i++) { rv = spi_flash_read(RECORD_ADDRESS, (uint8_t*)(&dev_record), sizeof(dev_record_t)); if (HAL_OK == rv) break; HAL_Delay(5000); } return rv; } /** * @brief 保存设备信息文件. * @param none * @retval HAL_xx. */ HAL_StatusTypeDef dev_info_save(void) { HAL_StatusTypeDef rv = HAL_OK; rv |= _dev_info_save(INFO_ADDRESS); rv |= _dev_info_save(INFO_ADDRESS_BAK); return rv; } /* 对需要写入内部flash的数据进行CRC32校验. */ void _updata_crc_check(uint32_t addr, uint32_t size) { uint16_t write_size = 0; uint32_t crc32_value = 0xffffffff; bin_pkt_tail_t *pkt_tail = NULL; /* 计算crc32. */ size = size - 4; while(size > 0) { /* 计算每次写入的数据大小. */ if (size > PACKET_1K_SIZE) write_size = PACKET_1K_SIZE; else write_size = size; /* 从spi flash读取升级包. */ if (spi_flash_read(addr, aPacketData, write_size) != HAL_OK) goto CRC_CHECK_ERROR; crc32_value = crc32_update(crc32_value, (char*)aPacketData, write_size); /* 重新计算flash地址和剩余数据大小. */ addr += write_size; size -= write_size; } /* 从spi flash读取报文头. */ if (spi_flash_read(addr + 4 - sizeof(bin_pkt_tail_t), aPacketData, sizeof(bin_pkt_tail_t)) != HAL_OK) goto CRC_CHECK_ERROR; pkt_tail = (bin_pkt_tail_t*)aPacketData; /* 比较报文头信息 */ if (crc32_value != pkt_tail->crc32 || dev_info.type_m != pkt_tail->dev_type_m || dev_info.type_s != pkt_tail->dev_type_s || pkt_tail->file_type != 2) { vty_print("Type %d %d %d %x %x\r\n", dev_info.type_m, dev_info.type_s, pkt_tail->file_type, pkt_tail->crc32, crc32_value); goto CRC_CHECK_ERROR; } return; CRC_CHECK_ERROR: Serial_PutString(" Programming failure start with old APP!\n\r"); /* 设置升级标志. */ dev_info.magic = UPDATE_ERROR_MAGIC; dev_info_save(); start_app(); } /* 擦除内部app,并升级app. */ static void _updata_process(void) { uint32_t spi_flash_address = TFTP_APP_ADDRESS; uint32_t flash_write_address = APP_ADDRESS; uint32_t fireware_size = dev_info.spi_fireware_size; uint16_t write_size = 0; uint16_t write_flash_size = 0; Serial_PutString("\r\nEntry updata.\r\n"); /* 擦除内部flash. */ if (FLASH_If_Erase(APP_ADDRESS) != FLASHIF_OK) goto UPDATA_ERROR; /* 循环写入内部flash. */ while(fireware_size > 0) { /* 计算每次写入的数据大小. */ if (fireware_size > PACKET_1K_SIZE) write_size = PACKET_1K_SIZE; else write_size = fireware_size; /* 从spi flash复制到内部flash. */ if (spi_flash_read(spi_flash_address, aPacketData, write_size) != HAL_OK) goto UPDATA_ERROR; /* 因为内部flash是4byte写的,如果img不是4byte对齐的可能最后几个字节写漏了,这里保证最后几个字节可以写入. */ write_flash_size = write_size / 4; if (write_size % 4 != 0) write_flash_size++; if (FLASH_If_Write(flash_write_address, (uint32_t*)(aPacketData), write_flash_size) != FLASHIF_OK) goto UPDATA_ERROR; /* 重新计算flash地址和剩余数据大小. */ spi_flash_address += write_size; flash_write_address += write_size; fireware_size -= write_size; } /* 设置升级标志. */ fireware_size = spi_flash_address - TFTP_APP_ADDRESS; dev_info.magic = INIT_DONE_MAGIC; dev_info.fireware_size = fireware_size; dev_info_save(); /* 打印升级信息. */ Serial_PutString(" Programming completed.\n\r"); Int2Str(number_str, fireware_size); Serial_PutString(" Size: "); Serial_PutString(number_str); Serial_PutString(" Bytes\r\n"); start_app(); UPDATA_ERROR: Serial_PutString(" Programming failure reset try again!\n\r"); /* 设置升级标志. */ dev_info.magic = UPDATE_ERROR_MAGIC; dev_info_save(); HAL_NVIC_SystemReset(); } /* 判断APP是否完整. */ static int32_t _is_app_intact(void) { bin_pkt_tail_t pkt_tail; uint32_t size = 0; uint32_t crc32_value = 0xffffffff; /* 大小不对. */ if (dev_info.fireware_size < 1024) return FALSE; /* 计算crc. */ size = dev_info.fireware_size - 4; crc32_value = crc32_update(crc32_value, (char*)APP_ADDRESS, size); /* 读取包头. */ memcpy(&pkt_tail, (void*)(APP_ADDRESS + dev_info.fireware_size - sizeof(bin_pkt_tail_t)), sizeof(bin_pkt_tail_t)); /* 比较报文头信息 */ if (crc32_value != pkt_tail.crc32 || dev_info.type_m != pkt_tail.dev_type_m || dev_info.type_s != pkt_tail.dev_type_s || pkt_tail.file_type != 2) { return FALSE; } return TRUE; } /** * @brief 选择进入哪种IAP处理方式. * @param none * @retval none */ void menu_entry(void) { uint8_t key = 0; uint8_t i = 0; /* 如果软件设置了网口升级, 进入网口升级. */ if (UPDATE_MAGIC == dev_info.magic) { _updata_crc_check(TFTP_APP_ADDRESS, dev_info.spi_fireware_size); _updata_process(); return; } /* 1. 如果APP校验不正确; 2. 看门狗重启; 3. 命令行iap模式重启. 以上3中情况则出现IAP启动进度条, 用户输入ctrl+P进入IAP, 超时自动启动APP. */ if (_is_app_intact() && dev_record.reset_type != RESET_IAP && dev_record.reset_type != RESET_WATCHDOG) { start_app(); } Serial_PutString("Wait to boot 3s..."); /* 等待串口命令, 是否强制进入IAP. */ for(i = 0; i < ENTRY_IAP_DELAY_COUNTER; i++) { if (HAL_UART_Receive(UartHandle, &key, 1, ENTRY_IAP_RX_TIMEOUT) != HAL_OK) continue; /* 键盘输入 "ctrl+P" 或者 "*" 进入串口 boot. */ if (CONTROL('P') == key || '*' == key) Main_Menu(); /* 键盘输入 "ctrl+U" 强制进入升级. */ else if(CONTROL('U') == key) { _updata_crc_check(TFTP_APP_ADDRESS, dev_info.spi_fireware_size); _updata_process(); return; } } /* 超时直接启动应用. */ start_app(); } void watchdog_refresh(void) { HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_RESET); } #if BYTE_ORDER == LITTLE_ENDIAN #if !defined(iap_htons) /** * Convert an u16_t from host- to network byte order. * * @param n u16_t in host byte order * @return n in network byte order */ uint16_t iap_htons(uint16_t n) { return (uint16_t)PP_HTONS(n); } #endif /* iap_htons */ #if !defined(iap_htonl) /** * Convert an u32_t from host- to network byte order. * * @param n u32_t in host byte order * @return n in network byte order */ uint32_t iap_htonl(uint32_t n) { return (uint32_t)PP_HTONL(n); } #endif /* iap_htonl */ #endif /* BYTE_ORDER == LITTLE_ENDIAN */ /************************ (C) COPYRIGHT LandPower *****END OF FILE****/