/***************************************************************************** * file Src/common.c * author YuLiang * version 1.0.0 * date 10-Jun-2022 * brief This file provides all the common related operation 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 WTOE 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 "usart.h" #include "FreeRTOS.h" #include "task.h" #include "cmsis_os.h" #include "rtc.h" #include "common.h" #include "dev_config.h" #include "cli.h" #include "flash_log.h" /* Private define ------------------------------------------------------------*/ /* CRC32 使用. */ #define CRC32_CHAR_BIT 8 /* 中断中打印限制长度. */ #define USART_PRINT_BUF_LEN 128 /* 公共监控任务标志位长度. */ #define COMMON_SYS_FLAG_LEN 4 /* Private typedef -----------------------------------------------------------*/ /* 公共监控任务操作参数. */ typedef struct { uint8_t flag[COMMON_SYS_FLAG_LEN]; uint32_t reset_delay; } common_sys_data_t; /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* 公共监控任务使用. */ const osThreadAttr_t COMMON_SYS_attributes = { .name = "COMMON_SYS", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityAboveNormal, }; static common_sys_data_t common_sys_data; static RTC_DateTypeDef common_date; static RTC_TimeTypeDef common_time; char *energy_str[ADC_ENERGY_CNT] = { "NONE", "NORMAL", "SLEEP", "FORCE" }; /* CRC使用. */ static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; static uint32_t crc32Table[256]; /* 软件看门狗标志. */ static uint32_t common_watchdog; static uint8_t is_watchdog_hw_reset = TRUE; /* 串口打印使用的端口. */ extern UART_HandleTypeDef *cli_uart; /* Private function prototypes -----------------------------------------------*/ /* Internal functions --------------------------------------------------------*/ /* 判断当情是否在中断中. */ static int _is_handler_mode(void) { return __get_IPSR() != 0; } /* 公共监控任务操作处理函数. */ static void _common_sys_handle(void) { if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_CONFIG)) { bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_CONFIG); dev_config_save(); } if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_RECORD)) { bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_RECORD); dev_record_save(); } if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_INFO)) { bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_INFO); dev_info_save(); } if (bit_list_tst(common_sys_data.flag, COM_SYS_RESET)) { if (0 == common_sys_data.reset_delay) { HAL_NVIC_SystemReset(); } else common_sys_data.reset_delay--; } if (bit_list_tst(common_sys_data.flag, COM_SYS_SHUTDOWN)) { bit_list_clr(common_sys_data.flag, COM_SYS_SHUTDOWN); system_shtudown(common_sys_data.reset_delay); } } /* 公共监控主任务循环. */ static void _common_sys_start(void *argument) { uint8_t count = 0; for (;;) { osDelay(250); /* 每 250ms 发送一次串口数据. */ cli_data_buf_send_hw(); /* 每 1s 处理一次配置和重启操作. */ if (0 == (count & 0x03)) { _common_sys_handle(); HAL_GPIO_TogglePin(LED_RUN_GPIO_Port, LED_RUN_Pin); common_watchdog_set(COM_WDG_COM); } if (is_watchdog_hw_reset) { is_watchdog_hw_reset = FALSE; HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_SET); osDelay(1); HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_RESET); vty_print("WDG\r\n"); } count++; } } /* Interface functions -------------------------------------------------------*/ void bit_list_set(uint8_t *bit_list, uint32_t bit) { bit_list[bit / 8] |= bit_mask[bit % 8]; } int bit_list_tst(uint8_t *bit_list, uint32_t bit) { return (bit_list[bit / 8] & bit_mask[bit % 8]) ? 1 : 0; } void bit_list_clr(uint8_t *bit_list, uint32_t bit) { bit_list[bit / 8] &= ~bit_mask[bit % 8]; } /* 在中断中发送通知. */ void notify_to_task(TaskHandle_t task) { BaseType_t higher_task_woken; /* xHigherPriorityTaskWoken 必须被初始化为 pdFALSE. 如果调用vTaskNotifyGiveFromISR() 会解除 vHandlingTask 任务的阻塞状, 并且 vHandlingTask 任务的优先级高于当前处于运行 状的任务, 则 xHigherPriorityTaskWoken 将会自动被设置为 pdTRUE. */ higher_task_woken = pdFALSE; /* 向一个任务发送通知, xHandlingTask 是该任务的句柄. */ vTaskNotifyGiveFromISR(task, &higher_task_woken); /* 如果 xHigherPriorityTaskWoken 为 pdTRUE, 则强制上下文切换. 这个宏的实现取决于移植层, 可能会调用 portEND_SWITCHING_ISR. */ portYIELD_FROM_ISR(higher_task_woken); } /* 在中断中发送通知, 带通知值. */ void notify_to_task_form_val(TaskHandle_t task, uint32_t val) { BaseType_t higher_task_woken; /* xHigherPriorityTaskWoken 必须被初始化为 pdFALSE. 如果调用 vTaskNotifyGiveFromISR() 会解除 vHandlingTask 任务的阻塞状, 并且 vHandlingTask 任务的优先级高于当前处于运行 状的任务, 则 xHigherPriorityTaskWoken 将会自动被设置为 pdTRUE. */ higher_task_woken = pdFALSE; /* 向一个任务发送通知,xHandlingTask是该任务的句柄. */ xTaskNotifyFromISR(task, val, eSetValueWithOverwrite, &higher_task_woken); /* 如果 xHigherPriorityTaskWoken 为 pdTRUE, 则强制上下文切换. 这个宏的实现取决于移植层, 可能会调用 portEND_SWITCHING_ISR. */ portYIELD_FROM_ISR(higher_task_woken); } /* 芯片进入睡眠模式, time 为设置的睡眠时间, 必须大于 1. */ void system_shtudown(uint32_t time) { /* 设置时间必须大于 1. */ if (0 == time) { time = 1; } vty_print_usart("SD %d\r\n", time); /* 设置为 RTC 唤醒. */ HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, time - 1, RTC_WAKEUPCLOCK_CK_SPRE_16BITS); /* 关闭系统时钟, 避免被系统时钟唤醒. */ HAL_SuspendTick(); /* 进入 shutdown 模式. */ HAL_PWR_EnterSTANDBYMode(); } uint8_t crc8(uint8_t *p, uint8_t len) { unsigned char crc = 0; unsigned char i, crc_1byte, abyte; while(len--) //len为总共要校验的字节数 { crc_1byte = 0; //设定crc_1byte初值为0 abyte = crc ^ *p++; for(i = 0; i < 8; i++) { if (((crc_1byte ^ abyte) & 0x01)) { crc_1byte ^= 0x18; crc_1byte >>= 1; crc_1byte |= 0x80; } else { crc_1byte >>= 1; } abyte >>= 1; } crc = crc_1byte; } return crc; } /* 计算CRC16/MODBUS. */ uint16_t crc16(uint8_t *data, uint16_t size) { uint16_t crc = 0xFFFF; uint8_t i = 0; while(size--) { crc = crc ^ *data++; for(i = 0; i < 8; i++) { if ((crc & 0x0001) > 0) { crc = crc >> 1; crc = crc ^ 0xa001; } else crc = crc >> 1; } } return crc; } 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; } /* crc32 初始化. */ 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 << (CRC32_CHAR_BIT - 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, CRC32_CHAR_BIT)] = crc32_reflect(remainder, 32); } while(++dividend); } uint32_t crc32(void const *buf, uint16_t byte_count) { uint32_t rem = 0xFFFFFFFF; uint8_t const * const b_begin = (uint8_t*)buf; uint8_t const * const b_end = b_begin + byte_count; for(uint8_t const *p = b_begin; p < b_end; p++) { uint8_t const byte_index = *p ^ rem; rem >>= CRC32_CHAR_BIT; rem ^= crc32Table[ byte_index ]; } return ~rem; } 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; } /* description: 在终端上输出格式化字符. * 注意: 不能在中断中使用. * param: 无. * return: 无. */ void vty_print(char *format, ...) { va_list ap; va_start(ap, format); if (IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI)) { if (IS_MONITOR_BIT_SET(system_init_flag, SYS_INIT_OS)) cli_usart_data_buf_send(format, ap); else cli_usart_data_buf_send_without_os(format, ap); } va_end(ap); } /* 打印错误信息vty. */ void vty_print_error(char const *func, int line, int rv) { vty_print("%s(%d): Error generation %d.\r\n", func, line, rv); } /* 通过 usart1 输出格式化字符. 原地等待发送, 与操作系统无关, * 可以在中断中使用, 尽量不要在中断中使用, 太耗时间. */ void vty_print_usart(char *format, ...) { static char buf[USART_PRINT_BUF_LEN]; if (IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI)) { va_list ap; va_start(ap, format); /* 中断只能在串口输出. */ if (_is_handler_mode() != 0) { taskDISABLE_INTERRUPTS(); if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0) { HAL_UART_AbortTransmit(cli_uart); HAL_UART_Transmit(cli_uart, (uint8_t*)buf, strlen(buf), 1000); } taskENABLE_INTERRUPTS(); } else { while(HAL_UART_STATE_BUSY_TX == cli_uart->gState) { continue; } if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0) { HAL_UART_Transmit(cli_uart, (uint8_t*)buf, strlen(buf), 1000); } } va_end(ap); } } /* 打印错误信息串口, 可以在中断中使用. */ void vty_print_error_usart(char const *func, int line, int rv) { vty_print_usart("Error generation %s(%d) is %d.\r\n", func, line, rv); } /* buf 16进制打印 */ void buf_print(uint8_t *buf, int32_t len) { int32_t i = 0; for(i = 0; i < len;) { vty_print("%02x ", buf[i++]); if(0 == i % 32) { vty_print("\r\n"); } } if(i % 32 != 0) { vty_print("\r\n"); } return; } /* buf 16进制打印 */ void buf_print_division(uint8_t *buf, int32_t len, uint16_t division) { int32_t i = 0; for(i = 0; i < len;) { vty_print("%02x ", buf[i++]); if(0 == i % division) { vty_print("\r\n"); } } if (i % division != 0) { vty_print("\r\n"); } return; } /* 公共监控任务初始化. */ void common_sys_init(void) { osThreadNew(_common_sys_start, NULL, &COMMON_SYS_attributes); } /* 公共监控任务操作接口. */ void common_sys_set(uint8_t flag, void *arg) { bit_list_set(common_sys_data.flag, flag); switch(flag) { case COM_SYS_RESET: MONITOR_BITMAP_SET(system_init_flag, SYS_INIT_RESET); common_sys_data.reset_delay = *(uint32_t*)arg; break; case COM_SYS_SHUTDOWN: if (!IS_MONITOR_BIT_SET(system_init_flag, SYS_INIT_SHUTDOWN)) { MONITOR_BITMAP_SET(system_init_flag, SYS_INIT_SHUTDOWN); common_sys_data.reset_delay = *(uint32_t*)arg; } break; default: break; } } /* 设置看门狗标记. */ void common_watchdog_set(uint32_t id) { common_watchdog |= id; } /* 清除看门狗标记. */ void common_watchdog_clr(void) { common_watchdog = 0; } /* 查询看门狗标记.0-没有置位,1-置位. */ int32_t common_watchdog_tst(uint32_t id) { return common_watchdog & id; } /* 查询看门狗标记.0-没有置位,1-置位. */ int32_t common_watchdog_tst_all(void) { uint32_t temp = COM_WDG_COM | COM_WDG_ADC | COM_WDG_CLI | COM_WDG_WIR; return ((common_watchdog & temp) == temp); } /* 通知程序喂硬件看门狗. */ void common_watchdog_hw_refresh(void) { is_watchdog_hw_reset = TRUE; } /* 计算绝对值. */ uint32_t abs_cal(int32_t a, int32_t b) { return (a > b ? a - b : b - a); } /* 计算绝对值. */ uint32_t abs_cal_u(uint32_t a, uint32_t b) { return (a > b ? a - b : b - a); } void rtc_time_set(uint32_t utc) { utc += 8 * 3600; struct tm *day = localtime(&utc); time_t temp = 0; HAL_RTC_GetTime(&hrtc, &time_structure, RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc, &date_structure, RTC_FORMAT_BIN); tm_structure.tm_year = date_structure.Year + 100; tm_structure.tm_mon = date_structure.Month - 1; tm_structure.tm_mday = date_structure.Date; tm_structure.tm_hour = time_structure.Hours; tm_structure.tm_min = time_structure.Minutes; tm_structure.tm_sec = time_structure.Seconds; temp = mktime(&tm_structure); if (abs_cal_u(temp, utc) >= 10) { vty_print("%ld %d\r\n", temp, utc); memset(&time_structure,0,sizeof(RTC_TimeTypeDef)); memset(&date_structure,0,sizeof(RTC_DateTypeDef)); time_structure.Hours = day->tm_hour; time_structure.Minutes = day->tm_min; time_structure.Seconds = day->tm_sec; HAL_RTC_SetTime(&hrtc, &time_structure, RTC_FORMAT_BIN); if (day->tm_year >= 100) { date_structure.Year = day->tm_year - 100; } date_structure.Month = day->tm_mon + 1; date_structure.Date = day->tm_mday; HAL_RTC_SetDate(&hrtc, &date_structure, RTC_FORMAT_BIN); } vty_print("%d/%d/%d %d:%d:%d\r\n", date_structure.Year, date_structure.Month, date_structure.Date, time_structure.Hours, time_structure.Minutes, time_structure.Seconds); } void restart_ontime(void) { uint32_t sleep_time = 65; HAL_RTC_GetTime(&hrtc, &common_time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc, &common_date, RTC_FORMAT_BIN); if (0 == dev_config.csg_reset_time[0]) { if (common_time.Hours == dev_config.csg_reset_time[1] && common_time.Minutes == dev_config.csg_reset_time[2]) { flash_log_write(FLASH_LOG_TYPE_WARNING, "Ontime restart %ds\r\n", sleep_time); common_sys_set(COM_SYS_SHUTDOWN, (void*)&sleep_time); } } else { if (common_date.Date == dev_config.csg_reset_time[0] && common_time.Hours == dev_config.csg_reset_time[1] && common_time.Minutes == dev_config.csg_reset_time[2]) { flash_log_write(FLASH_LOG_TYPE_WARNING, "Ontime restart %ds\r\n", sleep_time); common_sys_set(COM_SYS_SHUTDOWN, (void*)&sleep_time); } } } #if LD_DEBUG /* debug 模块使能 bitmap. */ static uint8_t dbg_module[DBG_BYTE_LEN]; /* 开指定模块的 debug. */ static void _dbg_on(uint8_t module) { bit_list_set(dbg_module, module); } /* 关指定模块的debug */ static void _dbg_off(uint8_t module) { bit_list_clr(dbg_module, module); } /* 关所有模块的debug */ static void _dbg_all_off(void) { memset(dbg_module, 0, DBG_BYTE_LEN); } /* 获取当前模块debug状态 */ int32_t dbg_stat_get(uint8_t module) { return bit_list_tst(dbg_module, module); } /* debug模块函数分发,flag=1代表是否是开启lwip debug. */ int32_t dbg_cmd_hander(uint8_t cmd, uint8_t data) { switch(cmd) { case DBG_CMD_ON: _dbg_on(data); break; case DBG_CMD_OFF: _dbg_off(data); break; case DBG_CMD_OFF_ALL: _dbg_all_off(); break; default: break; } return HAL_OK; } #endif #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 ****************/