commit 5401ebeb61442b20de42ded6edb7f0a8d43d0459 Author: xushenghao Date: Tue Dec 24 13:06:41 2024 +0800 first commit diff --git a/bsp/adcs.c b/bsp/adcs.c new file mode 100644 index 0000000..c1249d4 --- /dev/null +++ b/bsp/adcs.c @@ -0,0 +1,468 @@ +/** + * @file adcs.c + * @author xxx + * @date 2023-09-04 15:59:16 + * @brief LL库ADC驱动 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "adcs.h" +#include "dma.h" +adcs_t adcs[ADCS_MAX]; + +static uint8_t adc_get_channels_count(uint32_t channnels); // 通过用户配置的通道号获取通道数量 + +/** + * @brief ADC初始化 + * @param {adcs_e} num ADC编号 + * @param {ADC_TypeDef} *adc ADC外设 + * @param {DMA_TypeDef} *dma DMA外设 + * @param {uint32_t} dma_channel DMA通道 + * @param {uint8_t} adc_cct ADC采样次数 + * @param {uint32_t} channels 存储数据所在的序列号 + * @return {*} + * @note TCONV(转换时间) = (采样时间 + 12.5 个周期)/(主频/ADC分频系数) + */ +void adc_init(adcs_e num, ADC_TypeDef *adc, DMA_TypeDef *dma, uint32_t dma_channel, uint16_t adc_cct, uint32_t channels) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + DBG_ASSERT(adc != NULL __DBG_LINE); + DBG_ASSERT(dma != NULL __DBG_LINE); + DBG_ASSERT(adc_cct > 0 __DBG_LINE); + + adcs_t *p = &adcs[num]; + osel_memset((uint8_t *)p, 0, sizeof(adcs_t)); + p->adc = adc; + p->dma = dma; + p->dma_channel = dma_channel; + p->channels.data = channels; + p->adc_cct = adc_cct; + p->adc_chans_count = adc_get_channels_count(channels); + p->adc_sum = adc_cct * p->adc_chans_count; + +#if defined(SRAM2_BASE) // SRAM2速度更快 + p->adc_value = (uint16_t *)osel_mem_alloc2(sizeof(uint16_t) * p->adc_sum); +#else + p->adc_value = (uint16_t *)osel_mem_alloc(sizeof(uint16_t) * p->adc_sum); +#endif + + DBG_ASSERT(p->adc_value != NULL __DBG_LINE); + osel_memset((uint8_t *)p->adc_value, 0, sizeof(uint16_t) * p->adc_sum); + + uint32_t backup_setting_adc_dma_transfer = 0U; + backup_setting_adc_dma_transfer = LL_ADC_REG_GetDMATransfer(p->adc); + LL_ADC_REG_SetDMATransfer(p->adc, LL_ADC_REG_DMA_TRANSFER_NONE); + + // ADC开始校准 + LL_ADC_StartCalibration(p->adc, LL_ADC_SINGLE_ENDED); + // 等待校准完成 + while (LL_ADC_IsCalibrationOnGoing(p->adc)) + ; + LL_ADC_REG_SetDMATransfer(p->adc, backup_setting_adc_dma_transfer); + LL_mDelay(10); + LL_ADC_EnableIT_OVR(p->adc); + LL_ADC_Enable(p->adc); + LL_mDelay(10); + + if (BIT_IS_SET(channels, INVREF)) + { + // 使能VREFINT + LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(p->adc), LL_ADC_PATH_INTERNAL_VREFINT); + } + + LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum); + LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA)); + LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value); + LL_DMA_EnableChannel(p->dma, p->dma_channel); + + if (backup_setting_adc_dma_transfer == LL_ADC_REG_DMA_TRANSFER_UNLIMITED) + { + LL_ADC_REG_StartConversion(p->adc); // 开始转换 + } +} + +/** + * @brief 启动样本 + * + * 根据给定的 ADCS 枚举值启动 ADC 样本校准。 + * + * @param num ADCS 枚举值 + */ +void start_sample(adcs_e num) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + adcs_t *p = &adcs[num]; + LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum); + LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA)); + LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value); + LL_DMA_EnableChannel(p->dma, p->dma_channel); + + LL_ADC_ClearFlag_OVR(p->adc); + LL_ADC_ClearFlag_EOC(p->adc); + LL_ADC_REG_StartConversion(p->adc); // 开始转换 + + // ADC开始校准 + LL_ADC_StartCalibration(p->adc, LL_ADC_SINGLE_ENDED); +} + +/** + * @brief 停止样本采集 + * + * 根据给定的 ADCS 枚举值,停止相应的 ADCS 样本采集。 + * + * @param num ADCS 枚举值 + */ +void stop_sample(adcs_e num) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + adcs_t *p = &adcs[num]; + + LL_ADC_REG_StopConversion(p->adc); + while (LL_ADC_REG_IsConversionOngoing(p->adc) != 0) + ; + LL_DMA_DisableChannel(p->dma, p->dma_channel); + DMA_CLEAR_FLAG_TC_CHANNEL(p->dma, p->dma_channel); + DMA_CLEAR_FLAG_TE_CHANNEL(p->dma, p->dma_channel); +} + +/** + * @brief ADC重新开始转换 + * @param {adcs_e} num ADC编号 + * @return {*} + * @note + */ +void adc_restart(adcs_e num) +{ + stop_sample(num); + start_sample(num); +} + +/** + * @brief ADC反初始化 + * @param {adcs_e} num ADC编号 + * @return {*} + * @note + */ +void adc_dinit(adcs_e num) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + adcs_t *p = &adcs[num]; + LL_ADC_REG_StopConversion(p->adc); + LL_DMA_DisableChannel(p->dma, p->dma_channel); + LL_ADC_Disable(p->adc); + if (p->adc_value != NULL) + { +#if defined(SRAM2_BASE) + osel_mem_free2((uint16_t *)p->adc_value); +#else + osel_mem_free((uint16_t *)p->adc_value); +#endif + } +} +/** + * @brief 获取ADC转换结果,只需要第一个值 + * @param {adcs_e} num + * @param {uint8_t} chan + * @return {*} + * @note + */ +uint16_t adc_result_only_one(adcs_e num, uint8_t chan) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + uint16_t(*gram)[p->adc_chans_count] = (uint16_t(*)[p->adc_chans_count])p->adc_value; + return gram[0][chan]; +} + +/** + * @brief 中位值平均滤波,获取ADC转换结果 + * @param {adcs_e} num ADC编号 + * @param {uint8_t} chan 存储数据所在的序列号 + * @return {*} + * @note 不适合在中断中使用,因为排序算法时间复杂度为O(n^2) + */ +uint16_t adc_result_median_average(adcs_e num, uint8_t chan) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + + if (p->adc_cct <= 2) + return 0; // 如果adc_cct小于等于2,直接返回0 + p->median_average_ticks.uticks = sys_get_tick(); + uint16_t adc_temp[p->adc_cct]; + uint32_t adc_sum = 0; + uint16_t count = p->adc_cct >> 2; // 使用位移操作计算n的值 + + // 减少重复计算,计算基础偏移量 + uint16_t *adc_values = (uint16_t *)p->adc_value + chan; + for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count) + { + adc_temp[i] = *adc_values; + } + + insertion_sort(adc_temp, p->adc_cct); + + // 计算中间部分的和 + for (uint16_t i = count; i < p->adc_cct - count; ++i) + { + adc_sum += adc_temp[i]; + } + + // 计算平均值,确保不会除以0 + uint16_t res = adc_sum / (p->adc_cct - (count << 1)); + + p->median_average_ticks.ticks_current = sys_get_tick() - p->median_average_ticks.uticks; + if (p->median_average_ticks.ticks_current > p->median_average_ticks.ticks_max) + { + p->median_average_ticks.ticks_max = p->median_average_ticks.ticks_current; + } + + return res; +} + +/** + * @brief 中位值滤波,获取ADC转换结果 + * @param {adcs_e} num ADC编号 + * @param {uint8_t} chan 存储数据所在的序列号 + * @return {*} + * @note 不适合在中断中使用,因为排序算法时间复杂度为O(n^2) + */ +uint16_t adc_result_median(adcs_e num, uint8_t chan) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + uint16_t res = 0; + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + + p->median_ticks.uticks = sys_get_tick(); + + uint16_t adc_temp[p->adc_cct]; + // 减少重复计算,计算基础偏移量 + uint16_t *adc_values = (uint16_t *)p->adc_value + chan; + for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count) + { + adc_temp[i] = *adc_values; + } + insertion_sort(adc_temp, p->adc_cct); + res = adc_temp[p->adc_cct >> 1]; + + p->median_ticks.ticks_current = sys_get_tick() - p->median_ticks.uticks; + if (p->median_ticks.ticks_current > p->median_ticks.ticks_max) + { + p->median_ticks.ticks_max = p->median_ticks.ticks_current; + } + return res; +} + +/** + * @brief 平均值,获取ADC转换结果 + * @param {adcs_e} num ADC编号 + * @param {uint8_t} chan 存储数据所在的序列号 + * @return {*} + * @note + */ +uint16_t adc_result_average(adcs_e num, uint8_t chan) +{ + DBG_ASSERT(num < ADCS_MAX __DBG_LINE); + uint16_t res = 0; + uint32_t adc_sum = 0; + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + + p->average_ticks.uticks = sys_get_tick(); + + // 减少重复计算,计算基础偏移量 + uint16_t *adc_values = (uint16_t *)p->adc_value + chan; + for (uint16_t i = 0; i < p->adc_cct; ++i, adc_values += p->adc_chans_count) + { + adc_sum += *adc_values; + } + res = adc_sum / p->adc_cct; + + p->average_ticks.ticks_current = sys_get_tick() - p->average_ticks.uticks; + if (p->average_ticks.ticks_current > p->average_ticks.ticks_max) + { + p->average_ticks.ticks_max = p->average_ticks.ticks_current; + } + + return res; +} + +/** + * @brief 计算内部温度 + * @param {uint16_t} adc_value ADC转换结果 + * @return {*} + * @note 计算公式为:(measure * VDD_APPLI / VDD_CALIB) - (int32_t)*TEMP30_CAL_ADDR) * (int32_t)(130 - 30) / (int32_t)(*TEMP130_CAL_ADDR - *TEMP30_CAL_ADDR)) + 30 + * adc_value 是从ADC读取的温度传感器数据。 + TS_CAL1 是在30°C时校准的温度传感器数据,地址为TEMPSENSOR_CAL1_ADDR。 + TS_CAL2 是在110°C时校准的温度传感器数据,地址为TEMPSENSOR_CAL2_ADDR。 + TEMPSENSOR_CAL1_TEMP 是30°C。 + TEMPSENSOR_CAL2_TEMP 是110°C。 + */ +float32 adc_result_temperature(uint16_t adc_value) +{ + uint16_t ts_cal1 = *TEMPSENSOR_CAL1_ADDR; + uint16_t ts_cal2 = *TEMPSENSOR_CAL2_ADDR; + + float32 temperature = ((float32)(adc_value - ts_cal1) / (ts_cal2 - ts_cal1)) * (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP) + TEMPSENSOR_CAL1_TEMP; + return temperature; +} + +/** + * @brief 计算内部电压 + * @param {uint16_t} adc_value ADC转换结果 + * @return {*} 电压值V + * @note + */ +float32 adc_result_value_local(uint16_t adc_value) +{ + float32 vdd = (VREFINT_CAL_VREF * (*VREFINT_CAL_ADDR)) / 4095; + return ((vdd / adc_value) * 4095) / 1000; +} + +/** + * @brief ADC DMA转换回调函数 +{ + * @param {adcs_e} num ADC编号 + * @return {*} + * @note + */ +void adc_dma_callback(adcs_e num) +{ + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + if (LL_DMA_IsActiveFlag_TC1(p->dma) != 0) + { + LL_DMA_ClearFlag_TC1(p->dma); + // 停止ADC转换 + LL_ADC_REG_StopConversion(p->adc); + // 关闭ADC,可以不关闭但是校准无法清除 + // LL_ADC_Disable(p->adc); + } + // 检查DMA1的传输错误标志是否为1,如果是,则清除该标志 + if (LL_DMA_IsActiveFlag_TE1(p->dma) != 0) + { + LL_DMA_ClearFlag_TE1(p->dma); + } +} + +/** + * @brief ADC回调函数 + * @param {adcs_e} num ADC编号 + * @return {*} + * @note + */ +void adc_env_callback(adcs_e num) +{ + adcs_t *p = &adcs[num]; + DBG_ASSERT(p != NULL __DBG_LINE); + if (LL_ADC_IsActiveFlag_OVR(p->adc) != 0) + { + p->ovr_count++; + LL_ADC_REG_StopConversion(p->adc); + LL_DMA_DisableChannel(p->dma, p->dma_channel); + + LL_DMA_SetDataLength(p->dma, p->dma_channel, p->adc_sum); + LL_DMA_SetPeriphAddress(p->dma, p->dma_channel, LL_ADC_DMA_GetRegAddr(p->adc, LL_ADC_DMA_REG_REGULAR_DATA)); + LL_DMA_SetMemoryAddress(p->dma, p->dma_channel, (uint32_t)p->adc_value); + LL_DMA_EnableChannel(p->dma, p->dma_channel); + + LL_ADC_ClearFlag_OVR(p->adc); + LL_ADC_ClearFlag_EOC(p->adc); + LL_ADC_REG_StartConversion(p->adc); // 开始转换 + } +} + +/** + * @brief 通过用户配置的通道号获取通道数量 + * @param {uint32_t} channnels 存储数据所在的序列号 + * @return {*} + * @note + */ +static uint8_t adc_get_channels_count(uint32_t channnels) +{ + uint8_t ch_num = 0; + if (BIT_IS_SET(channnels, IN0)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN1)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN2)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN3)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN4)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN5)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN6)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN7)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN8)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN9)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN10)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN11)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN12)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN13)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN14)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN15)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, IN16)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, INVREF)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, INVBAT)) + { + ch_num++; + } + if (BIT_IS_SET(channnels, INTEMP)) + { + ch_num++; + } + return ch_num; +} diff --git a/bsp/adcs.h b/bsp/adcs.h new file mode 100644 index 0000000..a873a66 --- /dev/null +++ b/bsp/adcs.h @@ -0,0 +1,233 @@ +/** + * @file adcs.h + * @brief Header file for ADC driver using LL library. + * + * This file contains the declarations and documentation for the ADC driver + * using the LL (Low-Level) library. + * + * @date 2023-09-04 15:59:16 + * @author xxx + * @version 1.0 + * + * @note This code is proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + * + * @note All rights reserved by xxx. + */ + +#ifndef __ADCS_H__ +#define __ADCS_H__ + +#include "lib.h" +#include "main.h" +#include "sys.h" + +#define ADC_CHANNEL_MAX 18 ///< Maximum number of ADC channels +typedef enum +{ + IN0 = BIT0, + IN1 = BIT1, + IN2 = BIT2, + IN3 = BIT3, + IN4 = BIT4, + IN5 = BIT5, + IN6 = BIT6, + IN7 = BIT7, + IN8 = BIT8, + IN9 = BIT9, + IN10 = BIT10, + IN11 = BIT11, + IN12 = BIT12, + IN13 = BIT13, + IN14 = BIT14, + IN15 = BIT15, + IN16 = BIT16, + INTEMP = BIT17, + INVBAT = BIT18, + INVREF = BIT19, +} adc_num_e; ///< ADC channel number + +typedef enum +{ + ADCS_1, + ADCS_2, + ADCS_3, + ADCS_MAX, +} adcs_e; ///< ADC number + +typedef union +{ + uint32_t data; + struct + { + uint32_t in0 : 1; + uint32_t in1 : 1; + uint32_t in2 : 1; + uint32_t in3 : 1; + uint32_t in4 : 1; + uint32_t in5 : 1; + uint32_t in6 : 1; + uint32_t in7 : 1; + uint32_t in8 : 1; + uint32_t in9 : 1; + uint32_t in10 : 1; + uint32_t in11 : 1; + uint32_t in12 : 1; + uint32_t in13 : 1; + uint32_t in14 : 1; + uint32_t in15 : 1; + uint32_t invref : 1; + uint32_t intemp : 1; + }; +} adcs_channels_u; ///< ADC channels + +typedef struct +{ + ADC_TypeDef *adc; ///< ADC peripheral + DMA_TypeDef *dma; ///< DMA peripheral + uint32_t dma_channel; ///< DMA channel + adcs_channels_u channels; ///< ADC channels + uint32_t ovr_count; ///< ADC overflow count + + uint16_t adc_cct; ///< Channel single acquisition count + uint8_t adc_chans_count; ///< Number of channels + uint16_t adc_sum; ///< Channel acquisition count + __IO uint16_t *adc_value; ///< Address to store ADC conversion results + + utime_ticks_t median_average_ticks; ///< Median average filtering ticks + utime_ticks_t median_ticks; ///< Median filtering ticks + utime_ticks_t average_ticks; ///< Average filtering ticks +} adcs_t; + +/** + * @brief Initializes the ADC module. + * + * This function initializes the ADC module with the specified parameters. + * + * @param num The ADC number. + * @param adc Pointer to the ADC peripheral. + * @param dma Pointer to the DMA peripheral. + * @param dma_channel The DMA channel number. + * @param adc_cct The ADC continuous conversion mode. + * @param channels The number of ADC channels to be converted. + */ +extern void adc_init(adcs_e num, ADC_TypeDef *adc, DMA_TypeDef *dma, uint32_t dma_channel, uint16_t adc_cct, uint32_t channels); + +/** + * @brief Starts the ADC conversion. + * + * This function starts the ADC conversion for the specified ADC number. + * + * @param num The ADC number. + */ +extern void adc_restart(adcs_e num); + +/** + * @brief Deinitializes the ADC module. + * + * This function deinitializes the ADC module. + * + * @param num The ADC number. + */ +extern void adc_dinit(adcs_e num); + +/** + * @brief Gets the ADC conversion result for a single channel. + * + * This function gets the ADC conversion result for a single channel. + * It returns only the first converted value. + * + * @param num The ADC number. + * @param chan The ADC channel number. + * @return The ADC conversion result. + */ +extern uint16_t adc_result_only_one(adcs_e num, uint8_t chan); + +/** + * @brief Gets the ADC conversion result using median average filtering. + * + * This function gets the ADC conversion result for a single channel. + * It applies median average filtering to the converted values. + * + * @param num The ADC number. + * @param chan The ADC channel number. + * @return The ADC conversion result. + */ +extern uint16_t adc_result_median_average(adcs_e num, uint8_t chan); + +/** + * @brief Gets the ADC conversion result using median filtering. + * + * This function gets the ADC conversion result for a single channel. + * It applies median filtering to the converted values. + * + * @param num The ADC number. + * @param chan The ADC channel number. + * @return The ADC conversion result. + */ +extern uint16_t adc_result_median(adcs_e num, uint8_t chan); + +/** + * @brief Gets the ADC conversion result using average filtering. + * + * This function gets the ADC conversion result for a single channel. + * It applies average filtering to the converted values. + * + * @param num The ADC number. + * @param chan The ADC channel number. + * @return The ADC conversion result. + */ +extern uint16_t adc_result_average(adcs_e num, uint8_t chan); + +/** + * @brief Gets the ADC conversion result using N-times average filtering. + * + * This function gets the ADC conversion result for a single channel. + * It applies N-times average filtering to the converted values. + * + * @param num The ADC number. + * @param chan The ADC channel number. + * @return The ADC conversion result. + */ +extern uint16_t adc_result_n_average(adcs_e num, uint8_t chan); + +/** + * @brief Calculates the temperature from the ADC conversion result. + * + * This function calculates the temperature in degrees Celsius + * from the ADC conversion result of the internal temperature sensor. + * + * @param adc_value The ADC conversion result. + * @return The temperature in degrees Celsius. + */ +extern float32 adc_result_temperature(uint16_t adc_value); + +/** + * @brief Calculates the voltage from the ADC conversion result. + * + * This function calculates the voltage in millivolts + * from the ADC conversion result of the internal voltage reference. + * + * @param adc_value The ADC conversion result. + * @return The voltage in millivolts. + */ +extern float32 adc_result_value_local(uint16_t adc_value); + +/** + * @brief ADC environment callback function. + * + * This function is called when an ADC conversion is completed. + * + * @param num The ADC number. + */ +extern void adc_env_callback(adcs_e num); + +/** + * @brief DMA callback function for ADC. + * + * This function is called when a DMA transfer for ADC is completed. + * + * @param num The ADC number. + */ +extern void adc_dma_callback(adcs_e num); +#endif ///< __ADCS_H__ diff --git a/bsp/bsp.c b/bsp/bsp.c new file mode 100644 index 0000000..9e4ffee --- /dev/null +++ b/bsp/bsp.c @@ -0,0 +1,91 @@ + +#include "main.h" +#include "bsp.h" + +#define EXIT_LINE LL_EXTI_LINE_16 + +pvd_irq_handle_cb pvd_irq_handle_cb_func = NULL; +/** + * @brief 配置PVD(电源电压检测) + * + * 根据给定的电源电压等级配置PVD(电源电压检测)。 + * + * @param pwr_level 电源电压等级 + * @param call PVD中断处理回调函数 + */ +void pvd_configuration(uint32_t pwr_level, pvd_irq_handle_cb call) +{ + pvd_irq_handle_cb_func = call; + // 启用电源时钟 + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); + + // 设置PVD电平阈值,例如设置为2.4V LL_PWR_PVDLEVEL_2 + LL_PWR_SetPVDLevel(pwr_level); + + // 启用PVD + LL_PWR_EnablePVD(); + + // 配置PVD中断 + LL_EXTI_EnableIT_0_31(EXIT_LINE); // PVD连接到EXTI Line + LL_EXTI_EnableRisingTrig_0_31(EXIT_LINE); + LL_EXTI_EnableFallingTrig_0_31(EXIT_LINE); + + // 启用PVD中断向量 + // NVIC_EnableIRQ(PVD_PVM_IRQn); + // NVIC_SetPriority(PVD_PVM_IRQn, 0); +} + +/** + * @brief 处理PVD中断 + * + * 当PVD中断触发时,该函数将被调用以处理中断事件。 + * + * @note 无返回值 + */ +void pvd_irq_handle(void) +{ + if (LL_EXTI_IsActiveFlag_0_31(EXIT_LINE)) + { + LL_EXTI_ClearFlag_0_31(EXIT_LINE); + if (pvd_irq_handle_cb_func != NULL) + { + pvd_irq_handle_cb_func(); + } + } +} + +/** + * @brief 禁用调试接口 + * + * 禁用设备的调试接口,包括关闭调试停止模式、调试待机模式和调试睡眠模式。 + * 同时将 SWD 和 JTAG 接口的引脚配置为普通 GPIO。 + * + * @note disable_debug_interface调用后 SWD 接口会被关闭,ST-LINK 等调试工具无法通过 SWD 连接 MCU,也无法使用 STM32 ST-LINK Utility 软件 通过 ST-LINK 连接MCU解除读保护 + * 解除读保护流程:Read Out Protection : 改为 Level 0 ,调整为 Level 0 后 Flash 中的程序会被自动擦除。注意:千万不要改为 Level 2 ,改成Level 2 后 MCU 将会被彻底锁死,相当于熔断保护,无法通过软件再恢复。 + */ +void disable_debug_interface(void) +{ +// // 使能 SYSCFG 时钟 +// LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); + +// // 关闭调试接口 +// LL_DBGMCU_DisableDBGStopMode(); +// LL_DBGMCU_DisableDBGStandbyMode(); +// LL_DBGMCU_DisableDBGSleepMode(); + +// // 关闭 SWD 和 JTAG 接口 +// LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + +// // 配置 SWDIO (PA13) 和 SWCLK (PA14) 引脚为普通 GPIO +// GPIO_InitStruct.Pin = LL_GPIO_PIN_13 | LL_GPIO_PIN_14; +// GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG; +// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; +// LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + +// // 如果使用的是 JTAG 接口,还需要配置 JTDI (PA15), JTDO (PB3), 和 NJTRST (PB4) 引脚 +// GPIO_InitStruct.Pin = LL_GPIO_PIN_15; +// LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + +// GPIO_InitStruct.Pin = LL_GPIO_PIN_3 | LL_GPIO_PIN_4; +// LL_GPIO_Init(GPIOB, &GPIO_InitStruct); +} diff --git a/bsp/bsp.h b/bsp/bsp.h new file mode 100644 index 0000000..b0b3989 --- /dev/null +++ b/bsp/bsp.h @@ -0,0 +1,31 @@ +/** + * @file bsp.h + * @brief This file contains the declarations and definitions for the BSP (Board Support Package) module. + * + * The BSP module provides functions and configurations specific to the hardware platform, such as initializing + * peripherals, configuring GPIO pins, and managing interrupts. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __BSP_H__ +#define __BSP_H__ + +#include "gpios.h" +// #include "adcs.h" +// #include "dacs.h" +#include "dmas.h" +#include "tims.h" +#include "pwms.h" +#include "uarts.h" +#include "spis.h" +#include "i2cs.h" +// #include "eeprom.h" +///< 定义回调函数类型 +typedef void (*pvd_irq_handle_cb)(void); + +extern void pvd_configuration(uint32_t pwr_level, pvd_irq_handle_cb call); ///< Configures the Programmable Voltage Detector (PVD) module +extern void pvd_irq_handle(void); ///< Handles the PVD interrupt +extern void disable_debug_interface(void); ///< Disables the debug interface +#endif ///< __BSP_H__ diff --git a/bsp/dacs.c b/bsp/dacs.c new file mode 100644 index 0000000..2485e65 --- /dev/null +++ b/bsp/dacs.c @@ -0,0 +1,52 @@ +/** + * @file dacs.c + * @brief This file contains the implementation of the DAC module. + * It provides functions to initialize and de-initialize a DAC instance, + * as well as write a 16-bit value to the specified DAC channel. + * @author xxx + * @date 2023-12-27 14:44:03 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#include "dacs.h" + +/** + * @brief Writes a 16-bit value to the specified DAC channel + * + * @param dac pointer to the DAC instance + * @param value 16-bit value to write to the DAC + */ +static void _out(dac_t *dac, uint16_t value) +{ + DBG_ASSERT(dac != NULL __DBG_LINE); + DAC_OUT(dac->dac, dac->dac_channel, value); +} + +/** + * @brief Initializes a DAC instance + * + * @param dac pointer to the DAC instance + * @param dac_channel DAC channel to use + * @return pointer to the initialized DAC instance + */ +dac_t *dac_create(DAC_TypeDef *dac, uint16_t dac_channel) +{ + DBG_ASSERT(dac != NULL __DBG_LINE); + dac_t *handle = (dac_t *)osel_mem_alloc(sizeof(dac_t)); + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->dac = dac; + handle->dac_channel = dac_channel; + + handle->out = _out; + return handle; +} + +/** + * @brief De-initializes a DAC instance + * + * @param dac pointer to the DAC instance + */ +void dac_free(dac_t *dac) +{ + DBG_ASSERT(dac != NULL __DBG_LINE); + osel_mem_free(dac); +} diff --git a/bsp/dacs.h b/bsp/dacs.h new file mode 100644 index 0000000..56e68a7 --- /dev/null +++ b/bsp/dacs.h @@ -0,0 +1,55 @@ +/** + * @file dacs.h + * @brief Header file for DACs module. + * + * This file contains the declarations and definitions for the DACs module. + * DACs (Digital-to-Analog Converters) are used to convert digital signals into analog voltages. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __DACS_H__ +#define __DACS_H__ +#include "dac.h" +#include "lib.h" +#include "main.h" + +/** + * @brief Set the output value for a specific DAC channel. + * @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC. + * @param value: the output value to be set. + * @retval None + */ +#define DAC_OUT(DACx, DAC_Channel, Data) \ + do \ + { \ + LL_DAC_ConvertData12RightAligned(DACx, DAC_Channel, Data); \ + LL_DAC_TrigSWConversion(DACx, DAC_Channel); \ + } while (__LINE__ == -1) + +/** + * @brief Enable the specified DAC channel. + * @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC. + * @retval None + */ +#define DAC_START(DACx, DAC_Channel) LL_DAC_Enable(DACx, DAC_Channel) + +/** + * @brief Disable the specified DAC channel. + * @param dac: pointer to the @ref dac_t structure that contains the configuration information for the specified DAC. + * @retval None + */ +#define DAC_STOP(DACx, DAC_Channel) LL_DAC_Disable(DACx, DAC_Channel) + +/** + * @brief Structure definition for the DAC driver. + */ +typedef struct DACS +{ + DAC_TypeDef *dac; + uint16_t dac_channel; + + void (*out)(struct DACS *dac, uint16_t value); +} dac_t; +#endif diff --git a/bsp/dmas.h b/bsp/dmas.h new file mode 100644 index 0000000..6ef1a61 --- /dev/null +++ b/bsp/dmas.h @@ -0,0 +1,135 @@ +#ifndef __DMAS_H__ +#define __DMAS_H__ + +/** + * @brief 清除DMA传输完成标志 + * @param {DMA_HandleTypeDef} *DMAX DMA总线句柄 + * @param {uint32_t} CHx DMA通道号 + * @return {*} 操作结果 + * @note: 该宏用于清除DMA总线的传输完成标志。它首先检查DMA总线的传输完成标志是否已置位,如果已置位,则清除该标志。 + */ +#define DMA_ClEAR_FLAG_TC(DMAX, CHx) \ + do \ + { \ + if (LL_DMA_IsActiveFlag_TC##CHx(DMAX)) \ + { \ + LL_DMA_ClearFlag_TC##CHx(DMAX); \ + } \ + } while (__LINE__ == -1) + +/** + * @brief 清除DMA传输错误标志 + * @param {DMA_HandleTypeDef} *DMAX DMA总线句柄 + * @param {uint32_t} CHx DMA通道号 + * @return {*} 操作结果 + * @note: 该宏用于清除DMA总线的传输错误标志。它首先检查DMA总线的传输错误标志是否已置位,如果已置位,则清除该标志。 + */ +#define DMA_ClEAR_FLAG_TE(DMAX, CHx) \ + do \ + { \ + if (LL_DMA_IsActiveFlag_TE##CHx(DMAX)) \ + { \ + LL_DMA_ClearFlag_TE##CHx(DMAX); \ + } \ + } while (__LINE__ == -1) + +/** + * @file uarts.c + * @brief This file contains the implementation of DMA_CLEAR_FLAG_TC_CHANNEL macro. + */ + +/** + * @brief Clear the Transfer Complete (TC) flag of a specific DMA channel. + * + * @param dma The DMA peripheral. + * @param channel The DMA channel number. + */ +#define DMA_CLEAR_FLAG_TC_CHANNEL(dma, channel) \ + switch (channel) \ + { \ + case LL_DMA_CHANNEL_1: \ + DMA_ClEAR_FLAG_TC(dma, 1); \ + break; \ + case LL_DMA_CHANNEL_2: \ + DMA_ClEAR_FLAG_TC(dma, 2); \ + break; \ + case LL_DMA_CHANNEL_3: \ + DMA_ClEAR_FLAG_TC(dma, 3); \ + break; \ + case LL_DMA_CHANNEL_4: \ + DMA_ClEAR_FLAG_TC(dma, 4); \ + break; \ + case LL_DMA_CHANNEL_5: \ + DMA_ClEAR_FLAG_TC(dma, 5); \ + break; \ + case LL_DMA_CHANNEL_6: \ + DMA_ClEAR_FLAG_TC(dma, 6); \ + break; \ + case LL_DMA_CHANNEL_7: \ + DMA_ClEAR_FLAG_TC(dma, 7); \ + break; \ + default: \ + break; \ + } + +/** + * @brief Clear the Transfer Error (TE) flag for the specified DMA channel. + * + * @param dma The DMA peripheral. + * @param channel The DMA channel number. + */ +#define DMA_CLEAR_FLAG_TE_CHANNEL(dma, channel) \ + switch (channel) \ + { \ + case 1: \ + DMA_ClEAR_FLAG_TE(dma, 1); \ + break; \ + case 2: \ + DMA_ClEAR_FLAG_TE(dma, 2); \ + break; \ + case 3: \ + DMA_ClEAR_FLAG_TE(dma, 3); \ + break; \ + case 4: \ + DMA_ClEAR_FLAG_TE(dma, 4); \ + break; \ + case 5: \ + DMA_ClEAR_FLAG_TE(dma, 5); \ + break; \ + case 6: \ + DMA_ClEAR_FLAG_TE(dma, 6); \ + break; \ + case 7: \ + DMA_ClEAR_FLAG_TE(dma, 7); \ + break; \ + default: \ + break; \ + } + +/** + * @brief Clears the flags of a DMA channel + * @param DMAX DMAx register base + * @param CHx DMA channel number + * @param Flag a boolean variable that indicates if the transfer is complete + * @note This function should be called within the interrupt service routine of the DMA channel + */ +#define DMA_ClEAR_FLAG(DMAX, CHx, Flag) \ + do \ + { \ + if (LL_DMA_IsActiveFlag_TC##CHx(DMAX)) \ + { \ + LL_DMA_ClearFlag_TC##CHx(DMAX); \ + LL_DMA_ClearFlag_GI##CHx(DMAX); \ + Flag = TRUE; \ + } \ + if (LL_DMA_IsActiveFlag_TE##CHx(DMAX)) \ + { \ + LL_DMA_ClearFlag_TE##CHx(DMAX); \ + } \ + if (LL_DMA_IsActiveFlag_GI##CHx(DMAX)) \ + { \ + LL_DMA_ClearFlag_GI##CHx(DMAX); \ + } \ + } while (__LINE__ == -1) + +#endif // __DMAS_H__ diff --git a/bsp/eeprom.c b/bsp/eeprom.c new file mode 100644 index 0000000..6be9a42 --- /dev/null +++ b/bsp/eeprom.c @@ -0,0 +1,91 @@ +/** + * @file eeprom.c + * @author xxx + * @date 2023-11-16 10:28:47 + * @brief STM32L072xx EEPROM 驱动 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#include "eeprom.h" +#include "main.h" + +#ifdef STM32L072xx +#define PEKEY1 0x89ABCDEF // FLASH_PEKEYR +#define PEKEY2 0x02030405 // FLASH_PEKEYR + +#define LOCK __enable_irq(); // 系统开全局中断 +#define UNLOCK __disable_irq(); // 系统关全局中断 + +/** + * @brief 用于配置读取和写入EEPROM的函数 + * @param {uint32_t} read_addr - 读取地址 + * @param {uint8_t} *data - 存储数据的指针 + * @param {uint16_t} length - 读取或写入的数据长度 + * @return {*} + * @note: 这些函数用于在特定的地址读取或写入数据到EEPROM中。地址和数据以字节为单位,长度以字节为单位。 + */ +void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ + uint8_t *wAddr; + wAddr = (uint8_t *)(read_addr); + while (length--) + { + *data++ = *wAddr++; + } +} + +/** + * @brief 用于配置写入EEPROM的函数 + * @param {uint32_t} write_addr - 写入地址 + * @param {uint8_t} *data - 存储数据的指针 + * @param {uint16_t} length - 写入的数据长度 + * @return {*} + * @note: 这些函数用于在特定的地址写入数据到EEPROM中。地址和数据以字节为单位,长度以字节为单位。在写入数据之前,需要先解锁EEPROM,写入数据,然后锁定EEPROM。 + */ +void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ + uint8_t *addr; + addr = (uint8_t *)(write_addr); + UNLOCK + FLASH->PDKEYR = PEKEY1; + FLASH->PDKEYR = PEKEY2; + while (FLASH->PECR & FLASH_PECR_PELOCK) + ; + + while (length--) + { + *addr++ = *data++; + while (FLASH->SR & FLASH_SR_BSY) + ; + } + FLASH->PECR |= FLASH_PECR_PELOCK; + LOCK +} + +#endif // STM32L072xx + +#ifdef STM32L476xx + +/** + * @brief 用于配置读取和写入SRAM2的函数 + * @param {uint32_t} read_addr - 读取地址 + * @param {uint8_t} *data - 存储数据的指针 + * @param {uint16_t} length - 读取或写入的数据长度 + * @return {*} + * @note: 这些函数用于在特定的地址读取或写入数据到SRAM2中。地址和数据以字节为单位,长度以字节为单位。 + */ +void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ +} + +/** + * @brief 用于配置写入SRAM2的函数 + * @param {uint32_t} write_addr - 写入地址 从0开始 + * @param {uint8_t} *data - 存储数据的指针 + * @param {uint16_t} length - 写入的数据长度 + * @return {*} + * @note: 这些函数用于在特定的地址写入数据到SRAM2中。地址和数据以字节为单位,长度以字节为单位。 + */ +void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ +} +#endif // STM32L476xx diff --git a/bsp/eeprom.h b/bsp/eeprom.h new file mode 100644 index 0000000..42ea792 --- /dev/null +++ b/bsp/eeprom.h @@ -0,0 +1,7 @@ +#ifndef __EEPROM_H__ +#define __EEPROM_H__ +#include "lib.h" + +extern void chip_eeprom_config_read(uint32_t read_addr, uint8_t *data, uint16_t length); ///< 读取数据 +extern void chip_eeprom_config_write(uint32_t write_addr, uint8_t *data, uint16_t length); ///< 写入数据 +#endif ///< __EEPROM_H__ diff --git a/bsp/flash.c b/bsp/flash.c new file mode 100644 index 0000000..c1be5e1 --- /dev/null +++ b/bsp/flash.c @@ -0,0 +1,550 @@ +/** + * @file flash.c + * @author xxx + * @date 2024-02-07 11:49:34 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + * @attention + * + * ST 的官方驱动 LL 库并没有 flash 驱动。这里自己实现。 + * + * 1. 由于在 stm32l4xx_ll_system.h 中存在部分 FLASH 操作函数(ACR寄存器的处理)且不全面 + * 因此这里需要额外处理(重命名) + * + * 2. Main memory + * (1) FLASH_ACR 完成 + * (2) FLASH_PDKEYR 完成 + * (3) FLASH_KEYR 完成 + * (4) FLASH_OPTKEYR 完成 + * (5) FLASH_SR 完成 + * (6) FLASH_CR 完成 + * (7) FLASH_ECCR 完成 + * (8) FLASH_OPTR 未完成 + * (9) FLASH_PCROP1SR 未完成 + * 后续寄存器 均未完成 + * 3. Information block + * - System memory + * - OTP area + * - Option bytes + * 4. 根据 HAL 库的实现,相比于与手册的推荐流程,擦写的执行序列还有其他操作。 + * + * 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 "flash.h" +#include "stm32l4xx_ll_rcc.h" +#include "stm32l4xx_ll_system.h" +#include "stm32l4xx_ll_pwr.h" +#ifdef USE_FULL_ASSERT +#include "stm32_assert.h" +#else +#define assert_param(expr) ((void)0U) +#endif /* USE_FULL_ASSERT */ + +#define WHILE_MAX 30000U +/** @addtogroup STM32L4xx_LL_Driver + * @{ + */ + +/** @addtogroup FLASH_LL + * @{ + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @addtogroup FLASH_LL_Private_Constants + * @{ + */ + +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @addtogroup FLASH_LL_Private_Macros + * @{ + */ + +#define IS_LL_FLASH_WRITE_ADDR(__ADDR__) ((__ADDR__) % LL_FLASH_ALIGNMENT_MIN_SIZE == 0) + +/** + * @} + */ +/* Private function prototypes -----------------------------------------------*/ +/** @defgroup FLASH_LL_Private_Functions FLASH Private functions + * @{ + */ + +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup FLASH_LL_Exported_Functions + * @{ + */ +/** + * @brief Clear All Error in SR + * @param FLASHx FLASH Instance + * @retval None + */ +void LL_FLASH_ClearAllErrorFlag(void) +{ + LL_FLASH_ClearFlag_OPTVERR(FLASH); + LL_FLASH_ClearFlag_RDERR(FLASH); + LL_FLASH_ClearFlag_FASTERR(FLASH); + LL_FLASH_ClearFlag_MISERR(FLASH); + LL_FLASH_ClearFlag_PGSERR(FLASH); + LL_FLASH_ClearFlag_SIZERR(FLASH); + LL_FLASH_ClearFlag_PGAERR(FLASH); + LL_FLASH_ClearFlag_WRPERR(FLASH); + LL_FLASH_ClearFlag_PROGERR(FLASH); + LL_FLASH_ClearFlag_OPERR(FLASH); +} + +/** + * @brief Flush the instruction and data caches. + * @retval None + */ +void LL_FLASH_FlushCaches(void) +{ + /* Flush instruction cache */ + if (LL_FLASH_IsEnabledInstructionCache(FLASH)) + { + LL_FLASH_DisableInstructionCache(FLASH); + /* Reset instruction cache */ + LL_FLASH_InstructionCacheReset(FLASH); + /* Enable instruction cache */ + LL_FLASH_EnableInstructionCache(FLASH); + } + + /* Flush data cache */ + if (LL_FLASH_IsEnabledDataCache(FLASH)) + { + LL_FLASH_ZCS_DisableDataCache(FLASH); + /* Reset data cache */ + LL_FLASH_DataCacheReset(FLASH); + /* Enable data cache */ + LL_FLASH_ZCS_EnableDataCache(FLASH); + } +} + +/** + * @brief Erase Page + * @param pageno Page number + * @retval None + */ +ErrorStatus LL_FLASH_ErasePage(uint32_t pageno) +{ + uint16_t count = 0; + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */ + LL_FLASH_ClearAllErrorFlag(); + + /* Set the PER bit and select the page you wish to erase (PNB) with the associated bank (BKER) in the Flash control register (FLASH_CR). */ + LL_FLASH_EnablePageErase(FLASH); + + if (pageno >= LL_FLASH_BANK1_PAGE_NUM) + { + pageno -= LL_FLASH_BANK1_PAGE_NUM; + LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK2); + } + else + { + LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK1); + } + + LL_FLASH_SetErasePageNo(FLASH, pageno); + + /* Set the STRT bit in the FLASH_CR register. */ + LL_FLASH_EraseStart(FLASH); + + /* Wait for the BSY bit to be cleared in the FLASH_SR register. */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* 完成只有需要清除擦除标志. */ + LL_FLASH_DisablePageErase(FLASH); + + /* Flush the caches to be sure of the data consistency */ + LL_FLASH_FlushCaches(); + + return SUCCESS; +} + +/** + * @brief Erase bank + * @param bank This parameter can be one of the following values: + * @arg @ref LL_FLASH_BANK1 + * @arg @ref LL_FLASH_BANK2 + * @retval None + */ +ErrorStatus LL_FLASH_EraseBank(uint32_t bank) +{ + uint16_t count = 0; + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */ + LL_FLASH_ClearAllErrorFlag(); + + /* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR). + Both banks can be selected in the same operation. */ + if (bank == LL_FLASH_BANK1) + { + LL_FLASH_EnableBank1Erase(FLASH); + } + else + { + LL_FLASH_EnableBank2Erase(FLASH); + } + + /* Set the STRT bit in the FLASH_CR register. */ + LL_FLASH_EraseStart(FLASH); + + /* Wait for the BSY bit to be cleared in the FLASH_SR register. */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* 完成只有需要清除擦除标志. */ + LL_FLASH_DisableBank1Erase(FLASH); + LL_FLASH_DisableBank2Erase(FLASH); + + /* Flush the caches to be sure of the data consistency */ + LL_FLASH_FlushCaches(); + return SUCCESS; +} + +/** + * @brief Erase Chip + * @param None + * @retval None + */ +ErrorStatus LL_FLASH_EraseChip(void) +{ + uint16_t count = 0; + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */ + LL_FLASH_ClearAllErrorFlag(); + + /* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR). + Both banks can be selected in the same operation. */ + LL_FLASH_EnableBank1Erase(FLASH); + LL_FLASH_EnableBank2Erase(FLASH); + + /* Set the STRT bit in the FLASH_CR register. */ + LL_FLASH_EraseStart(FLASH); + + /* Wait for the BSY bit to be cleared in the FLASH_SR register. */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* 完成只有需要清除擦除标志. */ + LL_FLASH_DisableBank1Erase(FLASH); + LL_FLASH_DisableBank2Erase(FLASH); + + /* Flush the caches to be sure of the data consistency */ + LL_FLASH_FlushCaches(); + return SUCCESS; +} + +/** + * @brief Program Double Word + * @param address specifies the address to be programmed. + * @param data specifies the data to be programmed. + * @retval An ErrorStatus enumeration value: + * - SUCCESS: Write successfully + * - ERROR: error + */ +ErrorStatus LL_FLASH_ProgramDoubleWord(uint32_t address, uint64_t data) +{ + assert_param(!IS_LL_FLASH_WRITE_ADDR(address)); + uint16_t count = 0; + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + /* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */ + LL_FLASH_ClearAllErrorFlag(); + + /* Set the PG bit in the Flash control register (FLASH_CR). */ + LL_FLASH_EnableProgram(FLASH); + + /* Perform the data write operation at the desired memory address, inside main memory + block or OTP area. Only double word can be programmed. */ + /* Program the double word */ + *(__IO uint32_t *)address = (uint32_t)data; + *(__IO uint32_t *)(address + 4U) = (uint32_t)(data >> 32); + + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + /* Check that EOP flag is set in the FLASH_SR register (meaning that the programming + operation has succeed), and clear it by software. */ + if (LL_FLASH_IsActiveFlag_EOP(FLASH)) + { + LL_FLASH_ClearFlag_EOP(FLASH); + } + + /* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */ + LL_FLASH_DisableProgram(FLASH); + + /* Flush the caches to be sure of the data consistency */ + LL_FLASH_FlushCaches(); + + return SUCCESS; +} + +/** + * @brief Program + * @param address specifies the address to be programmed. + * @param data specifies the data to be programmed. + * @param num specifies the data number + * @retval An ErrorStatus enumeration value: + * - SUCCESS: Write successfully + * - ERROR: error + */ +ErrorStatus LL_FLASH_Program(uint32_t address, uint8_t data[], uint32_t num) +{ + static uint64_t DataT = 0; + uint32_t T = 0, S = 0; + uint16_t count = 0; + assert_param(!IS_LL_FLASH_WRITE_ADDR(address)); + + if (num == 0) + { + return SUCCESS; + } + + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + /* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */ + LL_FLASH_ClearAllErrorFlag(); + + /* Set the PG bit in the Flash control register (FLASH_CR). */ + LL_FLASH_EnableProgram(FLASH); + + /* Perform the data write operation at the desired memory address, inside main memory + block or OTP area. Only double word can be programmed. */ + T = num; + while (num > 0) + { + DataT = 0; + if (num >= 8) + { + for (int i = 0; i < LL_FLASH_ALIGNMENT_MIN_SIZE; i++) + { + DataT = DataT << 8; + DataT |= data[S + 7 - i]; + } + S += LL_FLASH_ALIGNMENT_MIN_SIZE; + num -= LL_FLASH_ALIGNMENT_MIN_SIZE; + } + else + { + for (int i = 0; i < num; i++) + { + DataT = DataT << 8; + DataT |= data[T - 1 - i]; + } + num = 0; + } + + /* Program the double word */ + *(__IO uint32_t *)address = (uint32_t)DataT; + *(__IO uint32_t *)(address + 4U) = (uint32_t)(DataT >> 32); + + /* Check that no Flash memory operation is ongoing by checking the BSY bit */ + while (LL_FLASH_IsActiveFlag_BSY(FLASH)) + { + if (count++ > WHILE_MAX) + { + return ERROR; + } + } + count = 0; + + /* Check that EOP flag is set in the FLASH_SR register (meaning that the programming + operation has succeed), and clear it by software. */ + if (LL_FLASH_IsActiveFlag_EOP(FLASH)) + { + LL_FLASH_ClearFlag_EOP(FLASH); + } + + address += LL_FLASH_ALIGNMENT_MIN_SIZE; + } + + /* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */ + LL_FLASH_DisableProgram(FLASH); + + /* Flush the caches to be sure of the data consistency */ + LL_FLASH_FlushCaches(); + + return SUCCESS; +} + +/** + * @} + */ + +/** @addtogroup FLASH_LL_Private_Functions + * @{ + */ +/** + * @brief Fast program a row double-word (64-bit) at a specified address. + * @param address: specifies the address to be programmed. + * @param DataAddress: specifies the address where the data are stored. + * @retval None + */ +void LL_FLASH_ProgramFast(uint32_t address, uint32_t DataAddress) +{ + uint8_t row_index = (2 * LL_FLASH_ROW_SIZE); + __IO uint32_t *dest_addr = (__IO uint32_t *)address; + __IO uint32_t *src_addr = (__IO uint32_t *)DataAddress; + + /* Check the parameters */ + assert_param(IS_FLASH_MAIN_MEM_ADDRESS(address)); + + /* Set FSTPG bit */ + LL_FLASH_EnableFastProgram(FLASH); + + /* Disable interrupts to avoid any interruption during the loop */ + __disable_irq(); + + /* Program the double word of the row */ + do + { + *dest_addr = *src_addr; + dest_addr++; + src_addr++; + row_index--; + } while (row_index != 0U); + + /* Re-enable the interrupts */ + __enable_irq(); + + LL_FLASH_DisableFastProgram(FLASH); +} + +/** + * @brief Fast program a row double-word (64-bit) at a specified address. + * @param address: specifies the address to be programmed. + * @param data: specifies the data to be programmed. + * @retval None + */ +ErrorStatus LL_FLASH_Read(uint32_t address, uint8_t data[], uint32_t num) +{ + if (num == 0) + { + return SUCCESS; + } + for (uint32_t i = 0; i < num; i++) + { + data[i] = *(__IO uint8_t *)(address + i); + } + + return SUCCESS; +} + +/** + * @brief 擦除指定地址的 FLASH + * + * 根据给定的地址和大小,擦除 FLASH 中指定范围的页面。 + * + * @param address FLASH 中的起始地址 + * @param size 需要擦除的大小(以字节为单位) + */ +void LL_FLASH_EraseAddress(uint32_t address, uint16_t size) +{ + uint16_t start_page = 0, end_page = 0; + start_page = address / LL_FLASH_PAGE_SIZE; + end_page = (address + size) / LL_FLASH_PAGE_SIZE; + // 擦除页 + for (uint16_t i = start_page; i < end_page; i++) + { + LL_FLASH_ErasePage(i); + } +} + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/bsp/flash.h b/bsp/flash.h new file mode 100644 index 0000000..b4b693a --- /dev/null +++ b/bsp/flash.h @@ -0,0 +1,1725 @@ +/** + * @file flash.h + * @author xxx + * @date 2024-02-07 11:49:34 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + * @attention + * + * ST 的官方驱动 LL 库并没有 flash 驱动。这里自己实现。 + * + * 1. 由于在 stm32l4xx_ll_system.h 中存在部分 FLASH 操作函数(ACR寄存器的处理)且不全面 + * 因此这里需要额外处理(重命名) + * + * 2. Main memory + * (1) FLASH_ACR 完成 + * (2) FLASH_PDKEYR 完成 + * (3) FLASH_KEYR 完成 + * (4) FLASH_OPTKEYR 完成 + * (5) FLASH_SR 完成 + * (6) FLASH_CR 完成 + * (7) FLASH_ECCR 完成 + * (8) FLASH_OPTR 未完成 + * (9) FLASH_PCROP1SR 未完成 + * 后续寄存器 均未完成 + * 3. Information block + * - System memory + * - OTP area + * - Option bytes + * 4. 根据 HAL 库的实现,相比于与手册的推荐流程,擦写的执行序列还有其他操作。 + * + * 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32L4xx_LL_FLASH_H +#define __STM32L4xx_LL_FLASH_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "osel_arch.h" + +/** @addtogroup STM32L4xx_LL_Driver + * @{ + */ + +/** @defgroup FLASH_LL FLASH + * @{ + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup FLASH_LL_Private_Constants FLASH Private Constants + * @{ + */ + +/* The following values must be written consecutively to unlock the FLASH_OPTR +register allowing option byte programming/erasing operations */ +#define LL_FLASH_OPT_KEY1 ((uint32_t)0x08192A3B) +#define LL_FLASH_OPT_KEY2 ((uint32_t)0x4C5D6E7F) + +/* The following values must be written consecutively to unlock the FLASH_CR +register allowing flash programming/erasing operations */ +#define LL_FLASH_KEY1 ((uint32_t)0x45670123) +#define LL_FLASH_KEY2 ((uint32_t)0xCDEF89AB) + +/* The following values must be written consecutively to unlock the RUN_PD bit in +FLASH_ACR */ +#define LL_FLASH_PDKEY1 ((uint32_t)0x04152637) +#define LL_FLASH_PDKEY2 ((uint32_t)0xFAFBFCFD) + +/* Page size */ +#define LL_FLASH_PAGE_SIZE (2 * 1024) + +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup FLASH_LL_Private_Macros FLASH Private Macros + * @{ + */ +/** + * @} + */ +/* Exported types ------------------------------------------------------------*/ +/** @defgroup FLASH_LL_ES_INIT FLASH Exported structures + * @{ + */ + +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup FLASH_LL_Exported_Constants FLASH Exported Constants + * @{ + */ + +/** @defgroup FLASH_LL_EC_BANK bank + * @{ + */ +#define LL_FLASH_BANK1 0 /*!< bank 1 */ +#define LL_FLASH_BANK2 1 /*!< bank 2 */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_ALIGNMENT alignment + * @{ + */ +#define LL_FLASH_ALIGNMENT_MIN_SIZE 8 /*!< the min alignment size */ +#define LL_FLASH_ROW_SIZE 32 /* row (32 double word) */ +#define LL_FLASH_BANK1_PAGE_NUM 256 /* */ +#define LL_FLASH_BANK2_PAGE_NUM 256 /* */ + +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_GET_FLAG Get Flags Defines + * @brief Flags defines which can be used with LL_FLASH_ReadReg function. + * @{ + */ +#define LL_FLASH_SR_BSY FLASH_SR_BSY /*!< Busy flag */ +#define LL_FLASH_SR_OPTVERR FLASH_SR_OPTVERR /*!< Option validity error flag */ +#define LL_FLASH_SR_RDERR FLASH_SR_RDERR /*!< PCROP read error flag */ +#define LL_FLASH_SR_FASTERR FLASH_SR_FASTERR /*!< Fast programming error flag */ +#define LL_FLASH_SR_MISERR FLASH_SR_MISERR /*!< Fast programming data miss error flag */ +#define LL_FLASH_SR_PGSERR FLASH_SR_PGSERR /*!< Programming sequence error flag */ +#define LL_FLASH_SR_SIZERR FLASH_SR_SIZERR /*!< Size error flag */ +#define LL_FLASH_SR_PGAERR FLASH_SR_PGAERR /*!< Programming alignment error flag */ +#define LL_FLASH_SR_WRPERR FLASH_SR_WRPERR /*!< Write protection error flag */ +#define LL_FLASH_SR_PROGERR FLASH_SR_PROGERR /*!< Programming error flag */ +#define LL_FLASH_SR_OPERR FLASH_SR_OPERR /*!< Operation error flag */ +#define LL_FLASH_SR_EOP FLASH_SR_EOP /*!< End of operation flag */ +/* */ +#define LL_FLASH_ECCR_DETECTION FLASH_ECCR_ECCD /*!< ECC detection */ +#define LL_FLASH_ECCR_CORRECTION FLASH_ECCR_ECCC /*!< ECC correction */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_IT IT Defines + * @brief IT defines which can be used with LL_FLASH_ReadReg and LL_FLASH_WriteReg functions + * @{ + */ +#define LL_FLASH_CR_RDERRIE FLASH_CR_RDERRIE /*!< PCROP read error interrupt enable */ +#define LL_FLASH_CR_ERRIE FLASH_CR_ERRIE /*!< Error interrupt enable */ +#define LL_FLASH_CR_EOPIE FLASH_CR_EOPIE /*!< End of operation interrupt enable */ +#define LL_FLASH_CR_ECCIE FLASH_ECCR_ECCIE /*!< ECC correction interrupt enable */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_Latency Flash Latency + * @{ + */ +#define LL_FLASH_LATENCY_0WS FLASH_ACR_LATENCY_0WS /*!< FLASH Zero Latency cycle */ +#define LL_FLASH_LATENCY_1WS FLASH_ACR_LATENCY_1WS /*!< FLASH One Latency cycle */ +#define LL_FLASH_LATENCY_2WS FLASH_ACR_LATENCY_2WS /*!< FLASH Two Latency cycles */ +#define LL_FLASH_LATENCY_3WS FLASH_ACR_LATENCY_3WS /*!< FLASH Three Latency cycles */ +#define LL_FLASH_LATENCY_4WS FLASH_ACR_LATENCY_4WS /*!< FLASH Four Latency cycles */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_PREFETCH Prefetch + * @{ + */ +#define LL_FLASH_PREFETCH_DISABLE 0x00000000U /*!< Prefetch disabled */ +#define LL_FLASH_PREFETCH_ENABLE FLASH_ACR_PRFTEN /*!< Prefetch enabled */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_INSTRUCTION_CACHE Instruction cache + * @{ + */ +#define LL_FLASH_INSTRUCTION_CACHE_DISABLE 0x00000000U /*!< Instruction cache disabled */ +#define LL_FLASH_INSTRUCTION_CACHE_ENABLE FLASH_ACR_ICEN /*!< Instruction cache enabled */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_DATA_CACHE data cache + * @{ + */ +#define LL_FLASH_DATA_CACHE_DISABLE 0x00000000U /*!< data cache disabled */ +#define LL_FLASH_DATA_CACHE_ENABLE FLASH_ACR_DCEN /*!< data cache enabled */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_RUN_PD_MODE Flash Power-down mode during Run or Low-power run mode + * @{ + */ +#define LL_FLASH_RUN_PD_MODE_IDLE 0x00000000U /*!< Flash in Idle mode */ +#define LL_FLASH_RUN_PD_MODE_PD FLASH_ACR_RUN_PD /*!< Flash in Power-down mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_SLEEP_PD_MODE Flash Power-down mode during Sleep or Low-power sleep mode + * @{ + */ +#define LL_FLASH_SLEEP_PD_MODE_IDLE 0x00000000U /*!< Flash in Idle mode during Sleep and Low-power sleep modes */ +#define LL_FLASH_SLEEP_PD_MODE_PD FLASH_ACR_SLEEP_PD /*!< Flash in Power-down mode during Sleep and Low-power sleep modes */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_PG Programming + * @{ + */ +#define LL_FLASH_PROGRAMMING_DISABLE 0x00000000U /*!< Flash programming disabled */ +#define LL_FLASH_PROGRAMMING_ENABLE FLASH_CR_PG /*!< Flash programming enabled */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_PER Programming + * @{ + */ +#define LL_FLASH_PAGE_ERASE_DISABLE 0x00000000U /*!< page erase disabled */ +#define LL_FLASH_PAGE_ERASE_ENABLE FLASH_CR_PER /*!< page erase enabled */ +/** + * @} + */ + +/** @defgroup FLASH_LL_EC_OBL_LAUNCH Force the option byte loading + * @{ + */ +#define LL_FLASH_OB_LAUNCH_COMPLETE 0x00000000U /*!< Option byte loading complete */ +#define LL_FLASH_OB_LAUNCH_REQUESTED FLASH_CR_OBL_LAUNCH /*!< Option byte loading requested */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_Read_Protection FLASH Option Bytes Read Protection + * @{ + */ +#define LL_FLASH_OB_RDP_LEVEL_0 ((uint32_t)0xAA) +#define LL_FLASH_OB_RDP_LEVEL_1 ((uint32_t)0xBB) +#define LL_FLASH_OB_RDP_LEVEL_2 ((uint32_t)0xCC) /*!< Warning: When enabling read protection level 2 it's no more possible to go back to level 1 or 0 */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_BOR_LEVEL FLASH Option Bytes BOR Level + * @{ + */ +#define LL_FLASH_OB_BOR_LEVEL_0 ((uint32_t)FLASH_OPTR_BOR_LEV_0) /*!< Reset level threshold is around 1.7V */ +#define LL_FLASH_OB_BOR_LEVEL_1 ((uint32_t)FLASH_OPTR_BOR_LEV_1) /*!< Reset level threshold is around 2.0V */ +#define LL_FLASH_OB_BOR_LEVEL_2 ((uint32_t)FLASH_OPTR_BOR_LEV_2) /*!< Reset level threshold is around 2.2V */ +#define LL_FLASH_OB_BOR_LEVEL_3 ((uint32_t)FLASH_OPTR_BOR_LEV_3) /*!< Reset level threshold is around 2.5V */ +#define LL_FLASH_OB_BOR_LEVEL_4 ((uint32_t)FLASH_OPTR_BOR_LEV_4) /*!< Reset level threshold is around 2.8V */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nRST_STOP FLASH Option Bytes Reset On Stop + * @{ + */ +#define LL_FLASH_OB_STOP_RST ((uint32_t)0x0000) /*!< Reset generated when entering the stop mode */ +#define LL_FLASH_OB_STOP_NORST ((uint32_t)FLASH_OPTR_nRST_STOP) /*!< No reset generated when entering the stop mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nRST_STANDBY FLASH Option Bytes Reset On Standby + * @{ + */ +#define LL_FLASH_OB_STANDBY_RST ((uint32_t)0x0000) /*!< Reset generated when entering the standby mode */ +#define LL_FLASH_OB_STANDBY_NORST ((uint32_t)FLASH_OPTR_nRST_STDBY) /*!< No reset generated when entering the standby mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nRST_SHUTDOWN FLASH Option Bytes Reset On Shutdown + * @{ + */ +#define LL_FLASH_OB_SHUTDOWN_RST ((uint32_t)0x0000) /*!< Reset generated when entering the shutdown mode */ +#define LL_FLASH_OB_SHUTDOWN_NORST ((uint32_t)FLASH_OPTR_nRST_SHDW) /*!< No reset generated when entering the shutdown mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_IWDG_SW FLASH Option Bytes IWDG Type + * @{ + */ +#define LL_FLASH_OB_IWDG_HW ((uint32_t)0x00000) /*!< Hardware independent watchdog */ +#define LL_FLASH_OB_IWDG_SW ((uint32_t)FLASH_OPTR_IWDG_SW) /*!< Software independent watchdog */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_IWDG_STOP FLASH Option Bytes IWDG Mode On Stop + * @{ + */ +#define LL_FLASH_OB_IWDG_STOP_FREEZE ((uint32_t)0x00000) /*!< Independent watchdog counter is frozen in Stop mode */ +#define LL_FLASH_OB_IWDG_STOP_RUN ((uint32_t)FLASH_OPTR_IWDG_STOP) /*!< Independent watchdog counter is running in Stop mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_IWDG_STANDBY FLASH Option Bytes IWDG Mode On Standby + * @{ + */ +#define LL_FLASH_OB_IWDG_STDBY_FREEZE ((uint32_t)0x00000) /*!< Independent watchdog counter is frozen in Standby mode */ +#define LL_FLASH_OB_IWDG_STDBY_RUN ((uint32_t)FLASH_OPTR_IWDG_STDBY) /*!< Independent watchdog counter is running in Standby mode */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_WWDG_SW FLASH Option Bytes WWDG Type + * @{ + */ +#define LL_FLASH_OB_WWDG_HW ((uint32_t)0x00000) /*!< Hardware window watchdog */ +#define LL_FLASH_OB_WWDG_SW ((uint32_t)FLASH_OPTR_WWDG_SW) /*!< Software window watchdog */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_BFB2 FLASH Option Bytes BFB2 Mode + * @{ + */ +#define LL_FLASH_OB_BFB2_DISABLE ((uint32_t)0x000000) /*!< Dual-bank boot disable */ +#define LL_FLASH_OB_BFB2_ENABLE ((uint32_t)FLASH_OPTR_BFB2) /*!< Dual-bank boot enable */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_DUALBANK FLASH Option Bytes Dual-bank Type + * @{ + */ +#define LL_FLASH_OB_DUALBANK_SINGLE ((uint32_t)0x000000) /*!< 256 KB/512 KB Single-bank Flash */ +#define LL_FLASH_OB_DUALBANK_DUAL ((uint32_t)FLASH_OPTR_DUALBANK) /*!< 256 KB/512 KB Dual-bank Flash */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nBOOT1 FLASH Option Bytes User BOOT1 Type + * @{ + */ +#define LL_FLASH_OB_BOOT1_SRAM ((uint32_t)0x000000) /*!< Embedded SRAM1 is selected as boot space (if BOOT0=1) */ +#define LL_FLASH_OB_BOOT1_SYSTEM ((uint32_t)FLASH_OPTR_nBOOT1) /*!< System memory is selected as boot space (if BOOT0=1) */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_SRAM2_PE FLASH Option Bytes User SRAM2 Parity Check Type + * @{ + */ +#define LL_FLASH_OB_SRAM2_PARITY_ENABLE ((uint32_t)0x0000000) /*!< SRAM2 parity check enable */ +#define LL_FLASH_OB_SRAM2_PARITY_DISABLE ((uint32_t)FLASH_OPTR_SRAM2_PE) /*!< SRAM2 parity check disable */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_SRAM2_RST FLASH Option Bytes User SRAM2 Erase On Reset Type + * @{ + */ +#define LL_FLASH_OB_SRAM2_RST_ERASE ((uint32_t)0x0000000) /*!< SRAM2 erased when a system reset occurs */ +#define LL_FLASH_OB_SRAM2_RST_NOT_ERASE ((uint32_t)FLASH_OPTR_SRAM2_RST) /*!< SRAM2 is not erased when a system reset occurs */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nSWBOOT0 FLASH Option Bytes User Software BOOT0 + * @{ + */ +#define LL_FLASH_OB_BOOT0_FROM_OB ((uint32_t)0x0000000) /*!< BOOT0 taken from the option bit nBOOT0 */ +#define LL_FLASH_OB_BOOT0_FROM_PIN ((uint32_t)FLASH_OPTR_nSWBOOT0) /*!< BOOT0 taken from PH3/BOOT0 pin */ +/** + * @} + */ + +/** @defgroup FLASH_LL_OB_nBOOT0 FLASH Option Bytes User nBOOT0 option bit + * @{ + */ +#define LL_FLASH_OB_BOOT0_RESET ((uint32_t)0x0000000) /*!< nBOOT0 = 0 */ +#define LL_FLASH_OB_BOOT0_SET ((uint32_t)FLASH_OPTR_nBOOT0) /*!< nBOOT0 = 1 */ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup CRS_LL_EM_WRITE_READ Common Write and read registers Macros + * @{ + */ + +/** + * @brief Write a value in FLASH register + * @param __INSTANCE__ FLASH Instance + * @param __REG__ Register to be written + * @param __VALUE__ Value to be written in the register + * @retval None + */ +#define LL_FLASH_WriteReg(__INSTANCE__, __REG__, __VALUE__) WRITE_REG(__INSTANCE__->__REG__, (__VALUE__)) + +/** + * @brief Read a value in FLASH register + * @param __INSTANCE__ FLASH Instance + * @param __REG__ Register to be read + * @retval Register value + */ +#define LL_FLASH_ReadReg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__) + /** + * @} + */ + + /* Exported functions --------------------------------------------------------*/ + /** @defgroup FLASH_LL_Exported_Functions FLASH Exported Functions + * @{ + */ + + /** @defgroup FLASH_LL_EF_Configuration Configuration + * @{ + */ + /** + * @brief Sets the code latency value + * @note When changing the CPU frequency, the following software sequences must be applied in + * order to tune the number of wait states needed to access the Flash memory + * @rmtoll ACR LATENCY LL_FLASH_SetLatency + * @param FLASHx Flash instance + * @param FLASH_Latency This parameter can be one of the following values: + * @arg @ref LL_FLASH_LATENCY_0WS + * @arg @ref LL_FLASH_LATENCY_1WS + * @arg @ref LL_FLASH_LATENCY_2WS + * @arg @ref LL_FLASH_LATENCY_3WS + * @arg @ref LL_FLASH_LATENCY_4WS + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ZCS_SetLatency(FLASH_TypeDef *FLASHx, uint32_t FLASH_Latency) + { + MODIFY_REG(FLASHx->ACR, FLASH_ACR_LATENCY, FLASH_Latency); + } + + /** + * @brief Sets the code latency value + * @rmtoll ACR LATENCY LL_FLASH_GetLatency + * @param FLASHx Flash instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_LATENCY_0WS + * @arg @ref LL_FLASH_LATENCY_1WS + * @arg @ref LL_FLASH_LATENCY_2WS + * @arg @ref LL_FLASH_LATENCY_3WS + * @arg @ref LL_FLASH_LATENCY_4WS + */ + __STATIC_INLINE uint32_t LL_FLASH_ZCS_GetLatency(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->ACR, FLASH_ACR_LATENCY)); + } + + /** + * @brief Enable FLASH Prefetch + * @rmtoll ACR PRFTEN LL_FLASH_EnablePrefetch + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ZCS_EnablePrefetch(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ACR, FLASH_ACR_PRFTEN); + } + + /** + * @brief Disable FLASH Prefetch + * @rmtoll ACR PRFTEN LL_FLASH_DisablePrefetch + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ZCS_DisablePrefetch(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->ACR, FLASH_ACR_PRFTEN); + } + + /** + * @brief Indicates whether the FLASH Prefetch is enabled. + * @rmtoll ACR PRFTEN LL_FLASH_IsEnabledPrefetch + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledPrefetch(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ACR, FLASH_ACR_PRFTEN) == (FLASH_ACR_PRFTEN)) ? 1UL : 0UL); + } + + /** + * @brief Enable FLASH Instruction cache + * @rmtoll ACR ICEN LL_FLASH_EnableInstructionCache + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableInstructionCache(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ACR, FLASH_ACR_ICEN); + } + + /** + * @brief Disable FLASH Instruction cache + * @rmtoll ACR ICEN LL_FLASH_DisableInstructionCache + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableInstructionCache(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->ACR, FLASH_ACR_ICEN); + } + + /** + * @brief Indicates whether the FLASH Instruction cache is enabled. + * @rmtoll ACR ICEN LL_FLASH_IsEnabledInstructionCache + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledInstructionCache(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ACR, FLASH_ACR_ICEN) == (FLASH_ACR_ICEN)) ? 1UL : 0UL); + } + + /** + * @brief Enable FLASH data cache + * @rmtoll ACR DCEN LL_FLASH_EnableDataCache + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ZCS_EnableDataCache(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ACR, FLASH_ACR_DCEN); + } + + /** + * @brief Disable FLASH data cache + * @rmtoll ACR DCEN LL_FLASH_DisableDataCache + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ZCS_DisableDataCache(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->ACR, FLASH_ACR_DCEN); + } + + /** + * @brief Indicates whether the FLASH data cache is enabled. + * @rmtoll ACR DCEN LL_FLASH_IsEnabledDataCache + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledDataCache(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ACR, FLASH_ACR_DCEN) == (FLASH_ACR_DCEN)) ? 1UL : 0UL); + } + + /** + * @brief Reset FLASH Instruction cache + * @note This bit can be written only when the instruction cache is disabled. + * @rmtoll ACR ICRST LL_FLASH_InstructionCacheReset + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_InstructionCacheReset(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ACR, FLASH_ACR_ICRST); + CLEAR_BIT(FLASHx->ACR, FLASH_ACR_ICRST); + } + + /** + * @brief Reset FLASH data cache + * @note This bit can be written only when the data cache is disabled. + * @rmtoll ACR DCRST LL_FLASH_DataCacheReset + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DataCacheReset(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ACR, FLASH_ACR_DCRST); + CLEAR_BIT(FLASHx->ACR, FLASH_ACR_DCRST); + } + + /** + * @brief Set Flash Power-down mode during Run or Low-power run mode + * @note This bit is write-protected with FLASH_PDKEYR. Call LL_FLASH_UnlockRunPowerDownMode before using this api + * @note The flash memory can be put in power-down mode only when the code is executed from RAM. + * @note The Flash must not be accessed when RUN_PD is set. + * @note he flash must not be put in power-down while a program or an erase operation is on-going. + * @rmtoll ACR RUN_PD LL_FLASH_SetRunPowerDownMode + * @param FLASHx FLASH Instance + * @param Mode This parameter can be one of the following values: + * @arg @ref LL_FLASH_RUN_PD_MODE_IDLE + * @arg @ref LL_FLASH_RUN_PD_MODE_PD + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetRunPowerDownMode(FLASH_TypeDef *FLASHx, uint32_t Mode) + { + MODIFY_REG(FLASHx->ACR, FLASH_ACR_RUN_PD, Mode); + } + + /** + * @brief Get Flash Power-down mode during Run or Low-power run mode + * @note This bit is write-protected with FLASH_PDKEYR. Call LL_FLASH_UnlockRunPowerDownMode before using this api + * @note The flash memory can be put in power-down mode only when the code is executed from RAM. + * @note The Flash must not be accessed when RUN_PD is set. + * @note he flash must not be put in power-down while a program or an erase operation is on-going. + * @rmtoll ACR RUN_PD LL_FLASH_GetRunPowerDownMode + * @param FLASHx FLASH Instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_RUN_PD_MODE_IDLE + * @arg @ref LL_FLASH_RUN_PD_MODE_PD + */ + __STATIC_INLINE uint32_t LL_FLASH_GetRunPowerDownMode(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->ACR, FLASH_ACR_RUN_PD)); + } + + /** + * @brief Set Flash Power-down mode during Sleep or Low-power sleep mode + * @note The flash must not be put in power-down while a program or an erase operation is on-going. + * @rmtoll ACR SLEEP_PD LL_FLASH_SetSleepPowerDownMode + * @param FLASHx FLASH Instance + * @param Mode This parameter can be one of the following values: + * @arg @ref LL_FLASH_SLEEP_PD_MODE_IDLE + * @arg @ref LL_FLASH_SLEEP_PD_MODE_PD + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetSleepPowerDownMode(FLASH_TypeDef *FLASHx, uint32_t Mode) + { + MODIFY_REG(FLASHx->ACR, FLASH_ACR_SLEEP_PD, Mode); + } + + /** + * @brief Get Flash Power-down mode during Sleep or Low-power sleep mode + * @rmtoll ACR SLEEP_PD LL_FLASH_SetSleepPowerDownMode + * @param FLASHx FLASH Instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_SLEEP_PD_MODE_IDLE + * @arg @ref LL_FLASH_SLEEP_PD_MODE_PD + */ + __STATIC_INLINE uint32_t LL_FLASH_GetSleepPowerDownMode(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->ACR, FLASH_ACR_SLEEP_PD)); + } + + /** + * @brief Unlock Power-down in Run mode + * @rmtoll PDKEYR LL_FLASH_SetSleepPowerDownMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_UnlockRunPowerDownMode(FLASH_TypeDef *FLASHx) + { + WRITE_REG(FLASHx->PDKEYR, LL_FLASH_PDKEY1); + WRITE_REG(FLASHx->PDKEYR, LL_FLASH_PDKEY2); + } + + /** + * @brief Sets Read protection level + * @rmtoll OPTR RDP LL_FLASH_SetReaProtectionLevel + * @param FLASHx Flash instance + * @param FLASH_Latency This parameter can be one of the following values: + * @arg @ref LL_FLASH_OB_RDP_LEVEL_0 + * @arg @ref LL_FLASH_OB_RDP_LEVEL_1 + * @arg @ref LL_FLASH_OB_RDP_LEVEL_2 + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetReaProtectionLevel(FLASH_TypeDef *FLASHx, uint32_t Level) + { + MODIFY_REG(FLASHx->ACR, FLASH_OPTR_RDP, Level); + } + + /** + * @brief Sets Read protection level + * @rmtoll OPTR RDP LL_FLASH_GetReaProtectionLevel + * @param FLASHx Flash instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_OB_RDP_LEVEL_0 + * @arg @ref LL_FLASH_OB_RDP_LEVEL_1 + * @arg @ref LL_FLASH_OB_RDP_LEVEL_2 + */ + __STATIC_INLINE uint32_t LL_FLASH_GetReaProtectionLevel(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->OPTR, FLASH_OPTR_RDP) >> FLASH_OPTR_RDP_Pos); + } + + /** + * @brief Sets BOR level + * @rmtoll OPTR BOR_LEV LL_FLASH_SetBORLevel + * @param FLASHx Flash instance + * @param FLASH_Latency This parameter can be one of the following values: + * @arg @ref LL_FLASH_OB_BOR_LEVEL_0 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_1 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_2 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_3 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_4 + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetBORLevel(FLASH_TypeDef *FLASHx, uint32_t Level) + { + MODIFY_REG(FLASHx->OPTR, FLASH_OPTR_BOR_LEV, Level); + } + + /** + * @brief Sets BOR level + * @rmtoll OPTR BOR_LEV LL_FLASH_GetBORLevel + * @param FLASHx Flash instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_OB_BOR_LEVEL_0 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_1 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_2 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_3 + * @arg @ref LL_FLASH_OB_BOR_LEVEL_4 + */ + __STATIC_INLINE uint32_t LL_FLASH_GetBORLevel(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->OPTR, FLASH_OPTR_BOR_LEV) >> FLASH_OPTR_BOR_LEV_Pos); + } + + /** + * @brief Enable Reset generated when entering the Stop mode + * @rmtoll OPTR nRST_STOP LL_FLASH_EnableResetStopMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableResetStopMode(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STOP); + } + + /** + * @brief Disable Reset generated when entering the Stop mode + * @rmtoll OPTR nRST_STOP LL_FLASH_DisableResetStopMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableResetStopMode(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STOP); + } + + /** + * @brief Indicates whether Reset generated when entering the Stop mode is enabled. + * @rmtoll OPTR nRST_STOP LL_FLASH_IsEnabledResetStopMode + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledResetStopMode(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STOP) == (FLASH_OPTR_nRST_STOP)) ? 1UL : 0UL); + } + + /** + * @brief Enable Reset generated when entering the Standby mode + * @rmtoll OPTR nRST_STOP LL_FLASH_EnableResetStandbyMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableResetStandbyMode(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STDBY); + } + + /** + * @brief Disable Reset generated when entering the Standby mode + * @rmtoll OPTR nRST_STOP LL_FLASH_DisableResetStandbyMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableResetStandbyMode(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STDBY); + } + + /** + * @brief Indicates whether Reset generated when entering the Standby mode is enabled. + * @rmtoll OPTR nRST_STOP LL_FLASH_IsEnabledResetStandbyMode + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledResetStandbyMode(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_STDBY) == (FLASH_OPTR_nRST_STDBY)) ? 1UL : 0UL); + } + + /** + * @brief Enable Reset generated when entering the Shutdown mode + * @rmtoll OPTR nRST_STOP LL_FLASH_EnableResetShutdownMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableResetShutdownMode(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_SHDW); + } + + /** + * @brief Disable Reset generated when entering the Shutdown mode + * @rmtoll OPTR nRST_STOP LL_FLASH_DisableResetShutdownMode + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableResetShutdownMode(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_SHDW); + } + + /** + * @brief Indicates whether Reset generated when entering the Shutdown mode is enabled. + * @rmtoll OPTR nRST_STOP LL_FLASH_IsEnabledResetShutdownMode + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledResetShutdownMode(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->OPTR, FLASH_OPTR_nRST_SHDW) == (FLASH_OPTR_nRST_SHDW)) ? 1UL : 0UL); + } + + /** + * @} + */ + + /** @defgroup FLASH_LL_EF_Programming Programming + * @{ + */ + /** + * @brief Unlocks the FLASH control register access + * @param FLASHx Flash instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_Unlock(FLASH_TypeDef *FLASHx) + { + WRITE_REG(FLASHx->KEYR, LL_FLASH_KEY1); + WRITE_REG(FLASHx->KEYR, LL_FLASH_KEY2); + } + + /** + * @brief Locks the FLASH control register access + * @rmtoll CR LOCK LL_FLASH_Lock + * @param FLASHx Flash instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_Lock(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_LOCK); + } + + /** + * @brief Unlocks the FLASH Option control register access + * @param FLASHx Flash instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_OB_Unlock(FLASH_TypeDef *FLASHx) + { + WRITE_REG(FLASHx->OPTKEYR, LL_FLASH_OPT_KEY1); + WRITE_REG(FLASHx->OPTKEYR, LL_FLASH_OPT_KEY2); + } + + /** + * @brief Locks the FLASH Option control register access + * @rmtoll CR OPTLOCK LL_FLASH_OB_Lock + * @param FLASHx Flash instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_OB_Lock(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_OPTLOCK); + } + + /** + * @brief Getthe option byte loading + * @rmtoll CR OBL_LAUNCH LL_FLASH_OB_GetLaunch + * @param FLASHx FLASH Instance + * @retval This parameter can be one of the following values: + * @arg @ref LL_FLASH_OB_LAUNCH_COMPLETE + * @arg @ref LL_FLASH_OB_LAUNCH_REQUESTED + */ + __STATIC_INLINE uint32_t LL_FLASH_OB_GetLaunch(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->CR, FLASH_CR_OBL_LAUNCH) >> FLASH_CR_OBL_LAUNCH_Pos); + } + + /** + * @brief Force the option byte loading + * @note When set to 1, this bit forces the option byte reloading. + * This bit is cleared only when the option byte loading is complete. + * @note It cannot be written if OPTLOCK is set. + * @rmtoll CR OBL_LAUNCH FLASH_OB_Launch + * @param FLASHx Flash instance + * @retval None + */ + __STATIC_INLINE void FLASH_OB_Launch(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_OBL_LAUNCH); + } + + /** + * @brief Enable FLASH Fast programming + * @rmtoll CR FSTPG LL_FLASH_EnableFastProgram + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableFastProgram(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_FSTPG); + } + + /** + * @brief Disable FLASH Fast programming + * @rmtoll CR FSTPG LL_FLASH_DisableFastProgram + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableFastProgram(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_FSTPG); + } + + /** + * @brief Indicates whether the FLASH Fast programming is enabled. + * @rmtoll CR FSTPG LL_FLASH_IsEnabledFastProgram + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledFastProgram(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_FSTPG) == (FLASH_CR_FSTPG)) ? 1UL : 0UL); + } + + /** + * @brief Enable FLASH Program + * @rmtoll CR PG LL_FLASH_EnableProgram + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableProgram(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_PG); + } + + /** + * @brief Disable FLASH Program + * @rmtoll CR PG LL_FLASH_DisableProgram + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableProgram(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_PG); + } + + /** + * @brief Indicates whether the FLASH Program is enabled. + * @rmtoll CR PRFTEN LL_FLASH_IsEnabledProgram + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledProgram(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_PG) == (FLASH_CR_PG)) ? 1UL : 0UL); + } + + /** + * @brief Set Page number MSB (bank selection) + * @rmtoll CR BKER LL_FLASH_SetErasePageBank + * @param FLASHx FLASH Instance + * @param Mode This parameter can be one of the following values: + * @arg @ref LL_FLASH_BANK1 + * @arg @ref LL_FLASH_BANK2 + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetErasePageBank(FLASH_TypeDef *FLASHx, uint32_t bank) + { + MODIFY_REG(FLASHx->CR, FLASH_CR_BKER, (bank << FLASH_CR_BKER_Pos)); + } + + /** + * @brief Get Page number MSB (bank selection) + * @rmtoll CR BKER LL_FLASH_GetErasePageBank + * @param FLASHx FLASH Instance + * @retval This parameter can be one of the following values: + * @arg @ref LL_FLASH_BANK1 + * @arg @ref LL_FLASH_BANK2 + */ + __STATIC_INLINE uint32_t LL_FLASH_GetErasePageBank(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->CR, FLASH_CR_BKER) >> FLASH_CR_BKER_Pos); + } + + /** + * @brief Set Page number selection + * @rmtoll CR PNB LL_FLASH_SetErasePageNo + * @param FLASHx FLASH Instance + * @param No This parameter can be one of the following values: + * @arg @ref 0 ~ 255 + * @retval None + */ + __STATIC_INLINE void LL_FLASH_SetErasePageNo(FLASH_TypeDef *FLASHx, uint32_t No) + { + MODIFY_REG(FLASHx->CR, FLASH_CR_PNB, ((No & 0xFFU) << FLASH_CR_PNB_Pos)); + } + + /** + * @brief Get Page number selection + * @rmtoll CR PNB LL_FLASH_SetErasePageNo + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE uint32_t LL_FLASH_GetErasePageNo(FLASH_TypeDef *FLASHx) + { + return (uint32_t)(READ_BIT(FLASHx->CR, FLASH_CR_PNB) >> FLASH_CR_PNB_Pos); + } + + /** + * @brief Clear Page number selection + * @rmtoll CR PNB LL_FLASH_ClearErasePageNo + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearErasePageNo(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_PNB); + } + + /** + * @brief Enable Page Erase + * @rmtoll CR PER LL_FLASH_EnablePageErase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnablePageErase(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_PER); + } + + /** + * @brief Disable Page Erase + * @rmtoll CR PER LL_FLASH_DisablePageErase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisablePageErase(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_PER); + } + + /** + * @brief Indicates whether the Page Erase is enabled. + * @rmtoll CR PER LL_FLASH_IsEnabledPageErase + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledPageErase(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_PER) == (FLASH_CR_PER)) ? 1UL : 0UL); + } + + /** + * @brief Enable Bank1 Erase + * @rmtoll CR MER1 LL_FLASH_EnableBank1Erase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableBank1Erase(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_MER1); + } + + /** + * @brief Disable FLASH Bank1Erase + * @rmtoll CR MER1 LL_FLASH_DisableBank1Erase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableBank1Erase(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_MER1); + } + + /** + * @brief Indicates whether the Bank1 Erase is enabled. + * @rmtoll CR MER1 LL_FLASH_IsEnabledBank1Erase + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledBank1Erase(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_MER1) == (FLASH_CR_MER1)) ? 1UL : 0UL); + } + + /** + * @brief Enable Bank2 Erase + * @rmtoll CR MER2 LL_FLASH_EnableBank2Erase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableBank2Erase(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_MER2); + } + + /** + * @brief Disable Bank2 Erase + * @rmtoll CR MER2 LL_FLASH_DisableBank2Erase + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableBank2Erase(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_MER2); + } + + /** + * @brief Indicates whether the Bank2 Erase is enabled. + * @rmtoll CR MER2 LL_FLASH_IsEnabledBank2Erase + * @param FLASHx Flash instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledBank2Erase(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_MER2) == (FLASH_CR_MER2)) ? 1UL : 0UL); + } + + /** + * @brief Start an erase operation + * @note If MER1, MER2 and PER bits are reset and the STRT bit is set, + * an unpredictable behavior may occur without generating any error flag. + * This condition should be forbidden. + * @note This bit is set only by software, and is cleared when the BSY bit is cleared in FLASH_SR. + * @rmtoll CR STRT LL_FLASH_EraseStart + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EraseStart(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_STRT); + } + + /** + * @brief Start an options operation + * @note This bit is set only by software, and is cleared when the BSY bit is cleared in FLASH_SR. + * @rmtoll CR STRT LL_FLASH_OprationStart + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_OB_OprationStart(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_OPTSTRT); + } + + /** + * @brief Get ECC fail bank + * @note When ECCC or ECCD is set, ADDR_ECC and BK_ECC are not updated if a new ECC error + * occurs. FLASH_ECCR is updated only when ECC flags are cleared. + * @rmtoll ECCR BK_ECC LL_FLASH_ECC_GetFailBank + * @param FLASHx FLASH Instance + * @retval Returned value can be one of the following values: + * @arg @ref LL_FLASH_BANK1 + * @arg @ref LL_FLASH_BANK2 + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_GetFailBank(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ECCR, FLASH_ECCR_BK_ECC) == (FLASH_ECCR_BK_ECC)) ? LL_FLASH_BANK2 : LL_FLASH_BANK1); + } + + /** + * @brief Get ECC fail addr + * @note When ECCC or ECCD is set, ADDR_ECC and BK_ECC are not updated if a new ECC error + * occurs. FLASH_ECCR is updated only when ECC flags are cleared. + * @rmtoll ECCR ADDR_ECC LL_FLASH_ECC_GetFailAddr + * @param FLASHx FLASH Instance + * @retval Value between Min_Data=0x00 and Max_Data=0x7FFFF + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_GetFailAddr(FLASH_TypeDef *FLASHx) + { + return (uint32_t)((READ_BIT(FLASHx->ECCR, FLASH_ECCR_ADDR_ECC) == (FLASH_ECCR_ADDR_ECC)) >> FLASH_ECCR_ADDR_ECC_Pos); + } + + /** + * @} + */ + + /** @defgroup FLASH_LL_EF_FLAG_Management FLAG_Management + * @{ + */ + /** + * @brief Get Busy flag + * @note This is set on the beginning of a Flash operation + * and reset when the operation finishes or when an error occurs. + * @rmtoll FLASH_SR BSY LL_FLASH_IsActiveFlag_BSY + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_BSY(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_BSY) == (FLASH_SR_BSY)) ? 1UL : 0UL); + } + + /** + * @brief Get Option validity error flag + * @note Set by hardware when the options read may not be the one configured by the user. + If option haven’t been properly loaded, OPTVERR is set again after each system reset. + * @rmtoll FLASH_SR OPTVERR LL_FLASH_IsActiveFlag_OPTVERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_OPTVERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_OPTVERR) == (FLASH_SR_OPTVERR)) ? 1UL : 0UL); + } + + /** + * @brief Get PCROP read error flag + * @note Set by hardware when an address to be read through the D-bus belongs to a + read protected area of the flash (PCROP protection). + @note An interrupt is generated if RDERRIE is set in FLASH_CR. + * @rmtoll FLASH_SR RDERR LL_FLASH_IsActiveFlag_RDERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_RDERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_RDERR) == (FLASH_SR_RDERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Fast programming error flag + * @note Set by hardware when a fast programming sequence (activated by FSTPG) is interrupted + * due to an error (alignment, size, write protection or data miss). + * @note The corresponding status bit (PGAERR, SIZERR, WRPERR or MISSERR) is set at the same time. + * @rmtoll FLASH_SR FASTERR LL_FLASH_IsActiveFlag_FASTERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_FASTERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_FASTERR) == (FLASH_SR_FASTERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Fast programming data miss error flag + * @note In Fast programming mode, 32 double words must be sent to flash successively, + and the new data must be sent to the flash logic control before the current data is fully programmed. + @note MISSERR is set by hardware when the new data is not present in time. + * @rmtoll FLASH_SR MISERR LL_FLASH_IsActiveFlag_MISERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_MISERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_MISERR) == (FLASH_SR_MISERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Programming sequence error flag + * @note Set by hardware when a write access to the Flash memory is performed by the code + * while PG or FSTPG have not been set previously. + * @note Set also by hardware when PROGERR, SIZERR, PGAERR, MISSERR or FASTERR is set + * due to a previous programming error. + * @rmtoll FLASH_SR PGSERR LL_FLASH_IsActiveFlag_PGSERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_PGSERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_PGSERR) == (FLASH_SR_PGSERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Size error flag + * @note Set by hardware when the size of the access is a byte or half-word + * during a program or a fast program sequence. + * @note Only double word programming is allowed (consequently: word access). + * @rmtoll FLASH_SR SIZERR LL_FLASH_IsActiveFlag_SIZERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_SIZERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_SIZERR) == (FLASH_SR_SIZERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Programming alignment error flag + * @note Set by hardware when the data to program cannot be contained + * in the same 64-bit Flash memory row in case of standard programming, + * or if there is a change of page during fast programming. + * @rmtoll FLASH_SR PGAERR LL_FLASH_IsActiveFlag_PGAERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_PGAERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_PGAERR) == (FLASH_SR_PGAERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Write protection error flag + * @note Set by hardware when an address to be erased/programmed + * belongs to a write-protected part (by WRP, PCROP or RDP level 1) of the Flash memory. + * @rmtoll FLASH_SR WRPERR LL_FLASH_IsActiveFlag_WRPERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_WRPERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_WRPERR) == (FLASH_SR_WRPERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Programming error flag + * @note Set by hardware when a double-word address to be programmed contains a value + * different from '0xFFFF FFFF' before programming, except if the data to write is '0x0000 0000'. + * @rmtoll FLASH_SR PROGERR LL_FLASH_IsActiveFlag_PROGERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_PROGERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_PROGERR) == (FLASH_SR_PROGERR)) ? 1UL : 0UL); + } + + /** + * @brief Get Operation error flag + * @note Set by hardware when a Flash memory operation (program / erase) completes unsuccessfully. + * @note This bit is set only if error interrupts are enabled (ERRIE = 1). + * @rmtoll FLASH_SR OPERR LL_FLASH_IsActiveFlag_OPERR + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_OPERR(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_OPERR) == (FLASH_SR_OPERR)) ? 1UL : 0UL); + } + + /** + * @brief Get End of operation flag + * @note Set by hardware when one or more Flash memory operation (programming / erase) has been completed successfully. + * @note This bit is set only if the end of operation interrupts are enabled (EOPIE = 1). + * @rmtoll FLASH_SR EOP LL_FLASH_IsActiveFlag_EOP + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsActiveFlag_EOP(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->SR, FLASH_SR_EOP) == (FLASH_SR_EOP)) ? 1UL : 0UL); + } + + /** + * @brief Clear Internal OPTVERR flag + * @rmtoll ISR IOPTVERR LL_FLASH_ClearFlag_OPTVERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_OPTVERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_OPTVERR); + } + + /** + * @brief Clear Internal RDERR flag + * @rmtoll ISR IRDERR LL_FLASH_ClearFlag_RDERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_RDERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_RDERR); + } + + /** + * @brief Clear Internal FASTERR flag + * @rmtoll ISR IFASTERR LL_FLASH_ClearFlag_FASTERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_FASTERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_FASTERR); + } + + /** + * @brief Clear Internal MISERR flag + * @rmtoll ISR IMISERR LL_FLASH_ClearFlag_MISERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_MISERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_MISERR); + } + + /** + * @brief Clear Internal PGSERR flag + * @rmtoll ISR IPGSERR LL_FLASH_ClearFlag_PGSERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_PGSERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_PGSERR); + } + + /** + * @brief Clear Internal SIZERR flag + * @rmtoll ISR ISIZERR LL_FLASH_ClearFlag_SIZERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_SIZERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_SIZERR); + } + + /** + * @brief Clear Internal PGAERR flag + * @rmtoll ISR IPGAERR LL_FLASH_ClearFlag_PGAERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_PGAERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_PGAERR); + } + + /** + * @brief Clear Internal WRPERR flag + * @rmtoll ISR IWRPERR LL_FLASH_ClearFlag_WRPERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_WRPERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_WRPERR); + } + + /** + * @brief Clear Internal PROGERR flag + * @rmtoll ISR IPROGERR LL_FLASH_ClearFlag_PROGERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_PROGERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_PROGERR); + } + + /** + * @brief Clear Internal OPERR flag + * @rmtoll ISR IOPERR LL_FLASH_ClearFlag_OPERR + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_OPERR(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_OPERR); + } + + /** + * @brief Clear Internal EOP flag + * @rmtoll ISR IEOP LL_FLASH_ClearFlag_EOP + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ClearFlag_EOP(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->SR, FLASH_SR_EOP); + } + + /** + * @brief Get Internal ECC ECCD flag + * @note Set by hardware when two ECC errors have been detected. When this bit is set, a NMI is generated + * @rmtoll ECCR ECCD LL_FLASH_ECC_IsActiveFlag_ECCD + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_IsActiveFlag_ECCD(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ECCR, FLASH_ECCR_ECCD) == (FLASH_ECCR_ECCD)) ? 1UL : 0UL); + } + + /** + * @brief Get Internal ECC ECCC flag + * @note Set by hardware when one ECC error has been detected and corrected. + * An interrupt is generated if ECCIE is set. + * @rmtoll ECCR ECCC LL_FLASH_ECC_IsActiveFlag_ECCC + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_IsActiveFlag_ECCC(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ECCR, FLASH_ECCR_ECCC) == (FLASH_ECCR_ECCC)) ? 1UL : 0UL); + } + + /** + * @brief Clear Internal ECCD flag + * @rmtoll ECCR ECCD LL_FLASH_ECC_ClearFlag_ECCD + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ECC_ClearFlag_ECCD(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ECCR, FLASH_ECCR_ECCD); + } + + /** + * @brief Clear Internal ECCC flag + * @rmtoll ECCR ECCC LL_FLASH_ECC_ClearFlag_ECCC + * @param FLASHx FLASH Instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ECC_ClearFlag_ECCC(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ECCR, FLASH_ECCR_ECCC); + } + + /** + * @brief Get Internal System Flash ECC fail flag + * @note This bit indicates that the ECC error correction or double ECC error detection + * is located in the System Flash. + * @rmtoll ECCR SYSF_ECC LL_FLASH_ECC_IsActiveFlag_SYSF_ECC + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_IsActiveFlag_SYSF_ECC(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ECCR, FLASH_ECCR_SYSF_ECC) == (FLASH_ECCR_SYSF_ECC)) ? 1UL : 0UL); + } + + /** + * @} + */ + + /** @defgroup FLASH_LL_EF_IT_Management IT-Management + * @{ + */ + /** + * @brief Enable PCROP read error interrupt (RDERRIE). + * @rmtoll CR RDERRIE LL_FLASH_EnableIT_RDERRIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableIT_RDERRIE(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_RDERRIE); + } + + /** + * @brief Disable PCROP read error interrupt (RDERRIE). + * @rmtoll CR RDERRIE LL_FLASH_DisableIT_RDERRIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableIT_RDERRIE(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_RDERRIE); + } + + /** + * @brief Enable Error interrupt (ERRIE). + * @rmtoll CR ERRIE LL_FLASH_EnableIT_ERRIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableIT_ERRIE(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_ERRIE); + } + + /** + * @brief Disable Error interrupt (ERRIE). + * @rmtoll CR ERRIE LL_FLASH_DisableIT_ERRIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableIT_ERRIE(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_ERRIE); + } + + /** + * @brief Enable End of operation interrupt (EOPIE). + * @rmtoll CR EOPIE LL_FLASH_EnableIT_EOPIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_EnableIT_EOPIE(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->CR, FLASH_CR_EOPIE); + } + + /** + * @brief Disable End of operation interrupt (EOPIE). + * @rmtoll CR EOPIE LL_FLASH_DisableIT_EOPIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_DisableIT_EOPIE(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->CR, FLASH_CR_EOPIE); + } + + /** + * @brief Indicates whether the PCROP read error interrupt (RDERRIE) is enabled. + * @rmtoll CR2 RDERRIE LL_FLASH_IsEnabledIT_RDERRIE + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledIT_RDERRIE(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_RDERRIE) == (FLASH_CR_RDERRIE)) ? 1UL : 0UL); + } + + /** + * @brief Indicates whether the Error interrupt (ERRIE) is enabled. + * @rmtoll CR2 ERRIE LL_FLASH_IsEnabledIT_ERRIE + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledIT_ERRIE(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_ERRIE) == (FLASH_CR_ERRIE)) ? 1UL : 0UL); + } + + /** + * @brief Indicates whether the End of operation interrupt (EOPIE) is enabled. + * @rmtoll CR2 EOPIE LL_FLASH_IsEnabledIT_EOPIE + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_IsEnabledIT_EOPIE(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->CR, FLASH_CR_EOPIE) == (FLASH_CR_EOPIE)) ? 1UL : 0UL); + } + + /** + * @brief Enable ECC correction interrupt (ECCIE). + * @rmtoll ECCR ECCIE LL_FLASH_ECC_EnableIT_ECCIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ECC_EnableIT_ECCIE(FLASH_TypeDef *FLASHx) + { + SET_BIT(FLASHx->ECCR, FLASH_ECCR_ECCIE); + } + + /** + * @brief Disable ECC correction interrupt (ECCIE). + * @rmtoll ECCR ECCIE LL_FLASH_ECC_DisableIT_ECCIE + * @param FLASHx FLASH instance + * @retval None + */ + __STATIC_INLINE void LL_FLASH_ECC_DisableIT_ECCIE(FLASH_TypeDef *FLASHx) + { + CLEAR_BIT(FLASHx->ECCR, FLASH_ECCR_ECCIE); + } + + /** + * @brief Indicates whether the ECC correction interrupt (ECCIE) is enabled. + * @rmtoll ECCR RDERRIE LL_FLASH_ECC_IsEnabledIT_ECCIE + * @param FLASHx FLASH Instance + * @retval State of bit (1 or 0). + */ + __STATIC_INLINE uint32_t LL_FLASH_ECC_IsEnabledIT_ECCIE(FLASH_TypeDef *FLASHx) + { + return ((READ_BIT(FLASHx->ECCR, FLASH_ECCR_ECCIE) == (FLASH_ECCR_ECCIE)) ? 1UL : 0UL); + } + + /** + * @} + */ + + /** @defgroup SPI_LL_EF_Init Initialization and de-initialization functions + * @{ + */ + void LL_FLASH_ClearAllErrorFlag(void); + void LL_FLASH_FlushCaches(void); + ErrorStatus LL_FLASH_ErasePage(uint32_t pageno); + ErrorStatus LL_FLASH_EraseBank(uint32_t bank); + ErrorStatus LL_FLASH_EraseChip(void); + ErrorStatus LL_FLASH_ProgramDoubleWord(uint32_t address, uint64_t data); + ErrorStatus LL_FLASH_Program(uint32_t address, uint8_t data[], uint32_t num); + ErrorStatus LL_FLASH_Read(uint32_t address, uint8_t data[], uint32_t num); + void LL_FLASH_ProgramFast(uint32_t address, uint32_t DataAddress); + void LL_FLASH_EraseAddress(uint32_t address, uint16_t size); + /** + * @} + */ + + /** + * @} + */ + + /** + * @} + */ + + /** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32L4xx_LL_FLASH_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/bsp/gpios.c b/bsp/gpios.c new file mode 100644 index 0000000..38abf89 --- /dev/null +++ b/bsp/gpios.c @@ -0,0 +1,76 @@ +#include "gpios.h" +#include "gpio.h" +/** + * @brief 设置GPIO引脚为高电平 + * @param {gpio_t} gpio - GPIO对象 + * @note: 用于设置指定GPIO引脚为高电平。 + */ +static void _set(gpio_t gpio) +{ + GPIO_SET(gpio.port, gpio.pin); +} + +/** + * @brief 设置GPIO引脚为低电平 + * @param {gpio_t} gpio - GPIO对象 + * @note: 用于设置指定GPIO引脚为低电平。 + */ +static void _reset(gpio_t gpio) +{ + GPIO_RESET(gpio.port, gpio.pin); +} + +/** + * @brief 切换GPIO引脚状态 + * @param {gpio_t} gpio - GPIO对象 + * @note: 用于切换指定GPIO引脚的状态,即高电平变为低电平,低电平变为高电平。 + */ +static void _toggle(gpio_t gpio) +{ + GPIO_TOGGLE(gpio.port, gpio.pin); +} + +/** + * @brief 读取GPIO引脚状态 + * @param {gpio_t} gpio - GPIO对象 + * @return {*} - GPIO引脚当前状态,即0表示低电平,1表示高电平 + * @note: 用于读取指定GPIO引脚的状态,即返回0或1。 + */ +static uint8_t _read(gpio_t gpio) +{ + return (uint8_t)GPIO_READ(gpio.port, gpio.pin); +} + +/** + * @brief 创建GPIO对象 + * @param {GPIO_TypeDef} *port - GPIO寄存器指针 + * @param {uint32_t} pin - 引脚号 + * @return {gpio_t *} - 创建的GPIO对象指针 + * @note: 用于创建一个GPIO对象,用于操作特定端口和引脚的GPIO功能。 + */ +gpio_t *gpio_create(GPIO_TypeDef *port, uint32_t pin) +{ + gpio_t *gpio = (gpio_t *)osel_mem_alloc(sizeof(gpio_t)); + DBG_ASSERT(gpio != NULL __DBG_LINE); + gpio->port = port; + gpio->pin = pin; + gpio->set = _set; + gpio->reset = _reset; + gpio->toggle = _toggle; + gpio->read = _read; + return gpio; +} + +/** + * @brief 释放GPIO对象 + * @param {gpio_t} *gpio - GPIO对象指针 + * @return {*} + * @note: 用于释放一个GPIO对象,释放后不能再使用该对象。 + */ +void gpio_free(gpio_t *gpio) +{ + if (gpio != NULL) + { + osel_mem_free(gpio); + } +} diff --git a/bsp/gpios.h b/bsp/gpios.h new file mode 100644 index 0000000..4433c62 --- /dev/null +++ b/bsp/gpios.h @@ -0,0 +1,145 @@ +/** + * @file gpios.h + * @brief Header file for GPIO configuration and control. + * + * This file contains the declarations and definitions for GPIO configuration and control functions. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @version 1.0 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ + +#ifndef __GPIOS_H__ +#define __GPIOS_H__ +#include "lib.h" +#include "main.h" + +/** + * @brief Set the GPIO pin to high. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_SET(port, pin) (LL_GPIO_SetOutputPin(port, pin)) + +/** + * @brief Set the GPIO pin to low. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_RESET(port, pin) (LL_GPIO_ResetOutputPin(port, pin)) + +/** + * @brief Toggle the state of the GPIO pin. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_TOGGLE(port, pin) (LL_GPIO_TogglePin(port, pin)) + +/** + * @brief Read the state of the GPIO pin. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + * @return The state of the GPIO pin (1 if high, 0 if low). + */ +#define GPIO_READ(port, pin) (LL_GPIO_IsInputPinSet(port, pin)) + +/** + * @brief Set the GPIO pin as input. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_SET_INPUT(port, pin) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_INPUT)) + +/** + * @brief Set the GPIO pin as output. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_SET_OUTPUT(port, pin) \ + do \ + { \ + LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_OUTPUT); \ + } while (0) + +/** + * @brief Set the GPIO pin as alternate function. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_SET_ALTERNATE(port, pin) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ALTERNATE)) + +/** + * @brief Set the GPIO pin as analog. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + */ +#define GPIO_SET_ANALOG(port, pin) \ + do \ + { \ + LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ANALOG); \ + } while (0) + +/** + * @brief Structure representing a GPIO pin. + */ +typedef struct GPIO +{ + GPIO_TypeDef *port; ///< The GPIO port. + uint32_t pin; ///< The GPIO pin. + + /** + * @brief Set the GPIO pin to high. + * + * @param gpio The GPIO pin. + */ + void (*set)(struct GPIO gpio); + + /** + * @brief Set the GPIO pin to low. + * + * @param gpio The GPIO pin. + */ + void (*reset)(struct GPIO gpio); + + /** + * @brief Toggle the state of the GPIO pin. + * + * @param gpio The GPIO pin. + */ + void (*toggle)(struct GPIO gpio); + + /** + * @brief Read the state of the GPIO pin. + * + * @param gpio The GPIO pin. + * @return The state of the GPIO pin (1 if high, 0 if low). + */ + uint8_t (*read)(struct GPIO gpio); +} gpio_t; + +/** + * @brief Create a GPIO pin. + * + * @param port The GPIO port. + * @param pin The GPIO pin. + * @return The created GPIO pin. + */ +extern gpio_t *gpio_create(GPIO_TypeDef *port, uint32_t pin); + +/** + * @brief Free the memory allocated for a GPIO pin. + * + * @param gpio The GPIO pin to free. + */ +extern void gpio_free(gpio_t *gpio); + +#endif ///< __GPIOS_H__ diff --git a/bsp/i2cs.c b/bsp/i2cs.c new file mode 100644 index 0000000..c6f53ba --- /dev/null +++ b/bsp/i2cs.c @@ -0,0 +1,669 @@ +#include "i2cs.h" +#include "main.h" + +static inline void delay(i2c_t *handle); // 延时函数 +static inline void _ack(i2c_t *handle); // 应答 +static inline void _nack(i2c_t *handle); // 非应答 + +/** + * @brief 启动I2C总线 + * @param {i2c_t} *handle - I2C总线句柄 + * @note: 用于启动I2C总线的操作。在发送或接收数据之前,需要先启动总线。 + */ +static void _start(i2c_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + // 获取gpios指针 + i2c_gpio_group_t *gpios = &handle->gpios; + // 获取scl指针 + gpio_t *scl = gpios->scl; + // 获取sda指针 + gpio_t *sda = gpios->sda; + // 设置sda + gpios->sda->set(*sda); + // 设置scl + gpios->scl->set(*scl); + // 延时 + delay(handle); + // 重置sda + gpios->sda->reset(*sda); + // 延时 + delay(handle); + // 重置scl + gpios->scl->reset(*scl); + // 延时 + delay(handle); +} + +/** + * @brief 停止I2C总线 + * @param {i2c_t} *handle - I2C总线句柄 + * @note: 用于停止I2C总线的操作。在发送或接收数据之后,需要先停止总线。 + */ +static void _stop(i2c_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + i2c_gpio_group_t *gpios = &handle->gpios; + gpio_t *scl = gpios->scl; + gpio_t *sda = gpios->sda; + gpios->scl->reset(*scl); + gpios->sda->reset(*sda); + delay(handle); + gpios->scl->set(*scl); + gpios->sda->set(*sda); + delay(handle); +} +/** + * @brief 等待应答信号 + * @param {i2c_t} *handle - I2C总线句柄 + * @return {BOOL} - 等待成功返回TRUE,否则返回FALSE + * @note: 用于等待I2C总线上发送的应答信号。 + */ +static BOOL _wait_ack(i2c_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + uint8_t count = 0; + i2c_gpio_group_t *gpios = &handle->gpios; + gpio_t *scl = gpios->scl; + gpio_t *sda = gpios->sda; + gpios->sda->set(*sda); + gpios->scl->set(*scl); + delay(handle); + while (gpios->sda->read(*sda)) + { + count++; + if (count > 250) + { + _stop(handle); + return FALSE; + } + } + gpios->scl->reset(*scl); + delay(handle); + return TRUE; +} + +/** + * @brief 读取一个字节 + * @param {i2c_t} *handle - I2C总线句柄 + * @param {BOOL} ack - 应答信号标志 + * @return {uint8_t} - 读取到的字节 + * @note: 用于从I2C总线上读取一个字节。在读取一个字节后,需要发送应答信号。 + */ +static uint8_t _read_byte(i2c_t *handle, BOOL ack) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + uint8_t i = 0, receive = 0; + i2c_gpio_group_t *gpios = &handle->gpios; + gpio_t *scl = gpios->scl; + gpio_t *sda = gpios->sda; + for (i = 0; i < 8; i++) + { + gpios->sda->set(*sda); + gpios->scl->set(*scl); + receive <<= 1; + delay(handle); + + if (gpios->sda->read(*sda)) + receive++; + + gpios->scl->reset(*scl); + delay(handle); + } + + if (TRUE == ack) + { + _ack(handle); + } + else + { + _nack(handle); + } + return receive; +} +/** + * @brief 发送一个字节的数据到I2C总线上 + * @param {i2c_t} *handle I2C总线的句柄 + * @param {uint8_t} data 要发送的字节数据 + * @return {*} 无 + * @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。 + */ +static void _write_byte(i2c_t *handle, uint8_t data) +{ + // 定义变量i + uint8_t i = 0; + // 断言参数handle不为空 + DBG_ASSERT(handle != NULL __DBG_LINE); + // 定义变量gpios + i2c_gpio_group_t *gpios = &handle->gpios; + // 定义变量scl + gpio_t *scl = gpios->scl; + // 定义变量sda + gpio_t *sda = gpios->sda; + // 遍历每一位 + for (i = 0; i < 8; i++) + { + // 如果data的最低位为1 + if (data & 0x80) + { + // 设置sda的状态为1 + gpios->sda->set(*sda); + } + // 否则,设置sda的状态为0 + else + { + // 设置sda的状态为0 + gpios->sda->reset(*sda); + } + + // 设置scl的状态为1 + gpios->scl->set(*scl); + // 延时1ms + delay(handle); + // 将data右移1位 + data <<= 1; + // 设置scl的状态为0 + gpios->scl->reset(*scl); + // 延时1ms + delay(handle); + + // 如果i等于7 + if (i == 7) + { + // 设置sda的状态为1 + gpios->sda->set(*sda); + } + } +} + +/** + * @brief 发送一个字节的数据到I2C总线上 + * @param {i2c_t} *handle I2C总线的句柄 + * @param {uint16_t} data 要发送的字节数据 + * @return {*} 无 + * @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。 + */ +static void _write_word(i2c_t *handle, uint16_t data) +{ + // 循环写入2个字节 + for (uint8_t i = 0; i < 2; i++) + { + // 将data的第i个字节写入i2c接口 + _write_byte(handle, (uint8_t)(data >> (8 * i))); + // 等待ACK + _wait_ack(handle); + } +} + +/** + * @brief 创建一个I2C总线设备 + * @param {i2c_gpio_group_t} gpios I2C总线的GPIO配置 + * @param {uint16_t} delay_ticks I2C总线的延时参数 + * @return {i2c_t *} 创建的I2C总线设备句柄 + * @note: 该函数用于创建一个I2C总线设备。它首先创建一个i2c_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。然后,它为handle结构体定义了start、stop、wait_ack、read_byte、write_byte和write_word函数。最后,它返回handle结构体。 + */ +i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks) +{ + // 创建一个i2c_t结构体 + i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t)); + // 将gpios的内存地址复制到handle结构体中 + osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(i2c_gpio_group_t)); + // 将delay_ticks的内存地址复制到handle结构体中 + handle->delay_ticks = delay_ticks; + + // 创建一个start函数 + handle->interface.start = _start; + // 创建一个stop函数 + handle->interface.stop = _stop; + // 创建一个wait_ack函数 + handle->interface.wait_ack = _wait_ack; + // 创建一个read_byte函数 + handle->interface.read_byte = _read_byte; + // 创建一个write_byte函数 + handle->interface.write_byte = _write_byte; + // 创建一个write_word函数 + handle->interface.write_word = _write_word; + + handle->dead_count = 0; + // 返回handle结构体 + return handle; +} + +/** + * @brief 设置I2C器件地址 + * @param {i2c_t} *handle + * @param {uint8_t} w_address 写地址 + * @param {uint8_t} r_address 读地址 + * @return {*} + * @note + */ +void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->w_address = w_address; + handle->r_address = r_address; +} + +/** + * @brief 释放I2C总线设备 + * @param {i2c_t} *handle 需要释放的I2C总线设备句柄 + * @return {*} 无 + * @note: 该函数用于释放一个I2C总线设备。它首先检查handle是否为空,如果不为空,则释放所有的GPIO。最后,释放handle的内存。 + */ +void i2c_free(i2c_t *handle) +{ + // 如果handle不为空,则释放所有的gpio + if (NULL != handle) + { + gpio_free(handle->gpios.scl); + gpio_free(handle->gpios.sda); + osel_mem_free(handle); + } +} + +/** + * @brief I2C DMA TX回调函数 + * @param {i2c_t} handle + * @return {*} + * @note + */ +void i2c_dma_callback(i2c_t *handle) +{ + // 检查输入参数是否为空 + DBG_ASSERT(handle != NULL __DBG_LINE); + // 清除传输完成标志位 + if (handle->dma_tx_cb != NULL) + { + handle->dma_tx_cb(handle); + } + if (handle->dma_rx_cb != NULL) + { + handle->dma_rx_cb(handle); + } +} + +#if defined(STM32L4xx_LL_I2C_H) +static void i2c_reset(i2c_t *handle); +static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); +static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); +/** + * @brief 创建一个I2C总线设备 DMA + * @param {I2C_TypeDef} *i2c + * @param {DMA_TypeDef} *dma + * @param {uint16_t} rxsize + * @param {uint32_t} dma_rx_channel + * @param {uint16_t} txsize + * @param {uint32_t} dma_tx_channel + * @return {*} + * @note + */ +i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel, + i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb) +{ + i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t)); + handle->i2c = i2c; + handle->dma = dma; + handle->dma_rx_channel = dma_rx_channel; + handle->dma_tx_channel = dma_tx_channel; + handle->rxbuf = (uint8_t *)osel_mem_alloc(rxsize); + handle->txbuf = (uint8_t *)osel_mem_alloc(txsize); + handle->rxsize = rxsize; + handle->txsize = txsize; + handle->tx_dma_ok = TRUE; + handle->interface.write_mem_dma = _write_mem_dma; + handle->interface.read_mem_dma = _read_mem_dma; + if (dma_rx_cb != NULL) + { + handle->dma_rx_cb = dma_rx_cb; + } + + if (dma_tx_cb != NULL) + { + handle->dma_tx_cb = dma_tx_cb; + } + + LL_DMA_DisableChannel(dma, dma_tx_channel); + LL_DMA_DisableChannel(dma, dma_rx_channel); + + // TX + uint8_t *pTransmitBuffer = handle->txbuf; + LL_DMA_ConfigTransfer(dma, dma_tx_channel, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE); + LL_DMA_ConfigAddresses(dma, dma_tx_channel, (uint32_t)pTransmitBuffer, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_TRANSMIT), LL_DMA_GetDataTransferDirection(dma, dma_tx_channel)); + LL_DMA_SetPeriphRequest(dma, dma_tx_channel, LL_DMA_REQUEST_3); + + // RX + uint8_t *pReceiveBuffer = handle->rxbuf; + LL_DMA_ConfigTransfer(dma, dma_rx_channel, LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE); + LL_DMA_ConfigAddresses(dma, dma_rx_channel, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_RECEIVE), (uint32_t)pReceiveBuffer, LL_DMA_GetDataTransferDirection(dma, dma_rx_channel)); + LL_DMA_SetPeriphRequest(dma, dma_rx_channel, LL_DMA_REQUEST_3); + + LL_DMA_EnableIT_TC(dma, dma_tx_channel); + LL_DMA_EnableIT_TE(dma, dma_tx_channel); + LL_DMA_EnableIT_TC(dma, dma_rx_channel); + LL_DMA_EnableIT_TE(dma, dma_rx_channel); + + LL_I2C_EnableDMAReq_TX(i2c); + LL_I2C_EnableDMAReq_RX(i2c); + LL_I2C_Enable(i2c); + + return handle; +} + +/** + * @brief 非阻塞模式下使用DMA从特定内存地址读取数据 + * @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息 + * @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口 + * @param {uint16_t} mem_address 内部内存地址 + * @param {uint16_t} mem_addsize 内部内存地址大小 + * @param {uint8_t} *data 数据缓冲区的指针 + * @param {uint16_t} size 要发送的数据量 + * @return {*} + * @note + */ +static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1) + { + i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题 + return FALSE; + } + uint16_t count = 2000; + handle->txsize = 0; + handle->tx_dma_ok = FALSE; + handle->rx_dma_ok = FALSE; + + for (uint8_t i = mem_addsize; i > 0; i--) + { + handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1))); + } + + LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize); + LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel); + + LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_SOFTEND, LL_I2C_GENERATE_START_WRITE); + count = 2000; + while (!handle->tx_dma_ok) + { + if (count-- == 0) + { + handle->tx_dma_ok = TRUE; + return FALSE; + } + } + + count = 2000; + while (LL_I2C_IsActiveFlag_TC(handle->i2c) != 1) + { + if (count-- == 0) + { + handle->tx_dma_ok = TRUE; + return FALSE; + } + } + + handle->tx_dma_ok = FALSE; + handle->rx_dma_ok = FALSE; + handle->rxsize = size; + osel_memset(handle->rxbuf, 0, handle->rxsize); + LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel); + LL_DMA_SetDataLength(handle->dma, handle->dma_rx_channel, handle->rxsize); + LL_DMA_EnableChannel(handle->dma, handle->dma_rx_channel); + LL_I2C_HandleTransfer(handle->i2c, handle->r_address, LL_I2C_ADDRSLAVE_7BIT, handle->rxsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_RESTART_7BIT_READ); + + count = 2000; + while (!LL_I2C_IsActiveFlag_STOP(handle->i2c)) + { + if (count-- == 0) + { + handle->tx_dma_ok = TRUE; + return FALSE; + } + } + + LL_DMA_DisableChannel(handle->dma, handle->dma_rx_channel); + LL_I2C_ClearFlag_STOP(handle->i2c); + osel_memcpy(data, handle->rxbuf, handle->rxsize); + return TRUE; +} + +/** + * @brief 非阻塞模式下使用DMA将数据写入特定内存地址 + * @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息 + * @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口 + * @param {uint16_t} mem_address 内部内存地址 + * @param {uint16_t} mem_addsize 内部内存地址大小 + * @param {uint8_t} *data 数据缓冲区的指针 + * @param {uint16_t} size 要发送的数据量 + * @return {*} + * @note + */ +static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + uint16_t count = 2000; + if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1) + { + i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题 + return FALSE; + } + + handle->txsize = 0; + handle->tx_dma_ok = FALSE; + for (uint8_t i = mem_addsize; i > 0; i--) + { + handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1))); + } + osel_memcpy(&handle->txbuf[handle->txsize], data, size); + handle->txsize += size; + LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize); + LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel); + + LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE); + + count = 2000; + while (!handle->tx_dma_ok) + { + if (count-- == 0) + { + handle->tx_dma_ok = TRUE; + return FALSE; + } + } + count = 2000; + while (!LL_I2C_IsActiveFlag_STOP(handle->i2c)) + { + if (count-- == 0) + { + handle->tx_dma_ok = TRUE; + return FALSE; + } + } + + LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel); + LL_I2C_ClearFlag_STOP(handle->i2c); + + return TRUE; +} + +/** + * @brief I2C重置 + * @param {I2C_TypeDef} *I2Cx + * @return {*} + * @note 解决I2C总线死锁问题 + */ +static void i2c_reset(i2c_t *handle) +{ + LL_I2C_Disable(handle->i2c); + LL_I2C_Enable(handle->i2c); + handle->dead_count++; +} + +/** + * @brief I2C 中断回调函数 + * @param {i2c_t} *handle + * @return {*} + * @note + */ +void i2c_ev_callback(i2c_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + if (LL_I2C_IsActiveFlag_ADDR(handle->i2c)) + { + /* Verify the Address Match with the OWN Slave address */ + if (LL_I2C_GetAddressMatchCode(handle->i2c) == handle->w_address || LL_I2C_GetAddressMatchCode(handle->i2c) == handle->r_address) + { + /* Verify the transfer direction, a write direction, Slave enters receiver mode */ + if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_WRITE) + { + /* Clear ADDR flag value in ISR register */ + LL_I2C_ClearFlag_ADDR(handle->i2c); + + /* Enable Receive Interrupt */ + LL_I2C_EnableIT_RX(handle->i2c); + } + else if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_READ) + { + /* Clear ADDR flag value in ISR register */ + LL_I2C_ClearFlag_ADDR(handle->i2c); + + /* Enable Transmit Interrupt */ + LL_I2C_EnableIT_TX(handle->i2c); + } + } + else + { + /* Clear ADDR flag value in ISR register */ + LL_I2C_ClearFlag_ADDR(handle->i2c); + + /* Call Error function */ + DBG_ASSERT(FALSE __DBG_LINE); + } + } + /* Check NACK flag value in ISR register */ + else if (LL_I2C_IsActiveFlag_NACK(handle->i2c)) + { + /* End of Transfer */ + LL_I2C_ClearFlag_NACK(handle->i2c); + } + /* Check RXNE flag value in ISR register */ + else if (LL_I2C_IsActiveFlag_RXNE(handle->i2c)) + { + /* Call function Slave Reception Callback */ + } + /* Check TXIS flag value in ISR register */ + else if (LL_I2C_IsActiveFlag_TXIS(handle->i2c)) + { + /* Call function Slave Ready to Transmit Callback */ + } + /* Check STOP flag value in ISR register */ + else if (LL_I2C_IsActiveFlag_STOP(handle->i2c)) + { + /* End of Transfer */ + LL_I2C_ClearFlag_STOP(handle->i2c); + + /* Check TXE flag value in ISR register */ + if (!LL_I2C_IsActiveFlag_TXE(handle->i2c)) + { + /* Flush TX buffer */ + LL_I2C_ClearFlag_TXE(handle->i2c); + } + + /* Call function Slave Complete Callback */ + } + /* Check TXE flag value in ISR register */ + else if (!LL_I2C_IsActiveFlag_TXE(handle->i2c)) + { + /* Do nothing */ + /* This Flag will be set by hardware when the TXDR register is empty */ + /* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register */ + } + else + { + /* Call Error function */ + DBG_ASSERT(FALSE __DBG_LINE); + } +} +#endif + +/*下面是内部实现方法*/ + +/** + * @brief 此方法是一个简单的延迟函数,它使用循环来执行指定数量的NOP(无操作)指令。这个延迟函数的目的是在程序的执行中引入延迟,这在需要精确定时的某些应用中很有用。延迟时间由输入参数“count”的值决定,该参数指定要执行的NOP指令的数量。延迟时间通常以微秒或毫秒为单位测量,具体取决于使用延迟的环境。 + * @param {uint16_t} count NOP指令的数量 + * @return {*} + */ +static inline void delay(i2c_t *handle) +{ + // 断言参数handle不为空 + DBG_ASSERT(handle != NULL __DBG_LINE); + // 定义循环计数变量count + uint16_t count = 0; + // 设置循环计数变量count的值为handle->delay_ticks + count = handle->delay_ticks; + // 循环计数变量count的值,直到count的值为0 + while (count--) + { + // 每次循环调用__NOP()函数 + __NOP(); + } +} + +/** + * @brief 发送ACK信号 + * @param {i2c_t} *handle I2C总线的句柄 + * @return {*} 无 + * @note: 该函数用于在I2C总线上发送ACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着重置sda,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。 + */ +static inline void _ack(i2c_t *handle) +{ + // 断言handle不为空 + DBG_ASSERT(handle != NULL __DBG_LINE); + // 获取gpios指针 + i2c_gpio_group_t *gpios = &handle->gpios; + // 获取scl指针 + gpio_t *scl = gpios->scl; + // 获取sda指针 + gpio_t *sda = gpios->sda; + + // 重置sda + gpios->sda->reset(*sda); + // 延时 + delay(handle); + // 设置scl + gpios->scl->set(*scl); + // 延时 + delay(handle); + // 重置scl + gpios->scl->reset(*scl); +} + +/** + * @brief 发送NACK信号 + * @param {i2c_t} *handle I2C总线的句柄 + * @return {*} 无 + * @note: 该函数用于在I2C总线上发送NACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着设置sda为1,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。 + */ +static inline void _nack(i2c_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + // 获取gpios指针 + i2c_gpio_group_t *gpios = &handle->gpios; + // 获取scl指针 + gpio_t *scl = gpios->scl; + // 获取sda指针 + gpio_t *sda = gpios->sda; + + // 设置sda引脚 + gpios->sda->set(*sda); + // 等待延时 + delay(handle); + // 设置scl引脚 + gpios->scl->set(*scl); + // 等待延时 + delay(handle); + // 重置scl引脚 + gpios->scl->reset(*scl); +} diff --git a/bsp/i2cs.h b/bsp/i2cs.h new file mode 100644 index 0000000..4f8d35a --- /dev/null +++ b/bsp/i2cs.h @@ -0,0 +1,127 @@ +/** + * @file i2cs.h + * @brief Header file for I2C Slave module. + * + * This file contains the declarations and definitions for the I2C Slave module. + * It provides functions to initialize and configure the I2C peripheral as a slave, + * as well as functions to send and receive data over the I2C bus. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @version 1.0 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ + +#ifndef __I2CS_H__ +#define __I2CS_H__ +#include "lib.h" +#include "gpios.h" +/** + * @file i2cs.h + * @brief Header file containing the definition of the I2C slave (I2CS) structure and related functions. + */ +typedef struct GPIO gpio_t; +typedef struct I2CS i2c_t; +typedef void i2cs_dma_callback(i2c_t *handle); + +typedef struct +{ + void (*start)(i2c_t *handle); ///< Function pointer to start the I2C communication. + void (*stop)(i2c_t *handle); ///< Function pointer to stop the I2C communication. + BOOL(*wait_ack) + (i2c_t *handle); ///< Function pointer to wait for the acknowledgment from the I2C bus. + + void (*write_byte)(i2c_t *handle, uint8_t data); ///< Function pointer to write a byte of data to the I2C bus. + uint8_t (*read_byte)(i2c_t *handle, BOOL ack); ///< Function pointer to read a byte of data from the I2C bus. + + void (*write_word)(i2c_t *handle, uint16_t data); ///< Function pointer to write two bytes of data to the I2C bus. + + BOOL(*write_mem_dma) + (i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to write multiple bytes of data to a memory address using DMA. + BOOL(*read_mem_dma) + (i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to read multiple bytes of data from a memory address using DMA. +} i2c_interface_t; + +typedef struct +{ + struct GPIO *scl; ///< Pointer to the GPIO pin used for the I2C clock (SCL). + struct GPIO *sda; ///< Pointer to the GPIO pin used for the I2C data (SDA). +} i2c_gpio_group_t; + +struct I2CS +{ + ///< Analog part definition + i2c_gpio_group_t gpios; ///< Structure containing the GPIO pins used for the I2C communication. + uint16_t delay_ticks; ///< Number of NOP instructions to delay the I2C communication. + + ///< Hardware part definition + I2C_TypeDef *i2c; ///< Pointer to the I2C peripheral. + DMA_TypeDef *dma; ///< Pointer to the DMA peripheral. + uint32_t dma_rx_channel; ///< DMA channel used for receiving data. + uint32_t dma_tx_channel; ///< DMA channel used for transmitting data. + uint8_t *rxbuf; ///< Pointer to the receive buffer. + uint16_t rxsize; ///< Size of the receive buffer. + uint8_t *txbuf; ///< Pointer to the transmit buffer. + uint16_t txsize; ///< Size of the transmit buffer. + uint8_t w_address; ///< 7-bit write address. + uint8_t r_address; ///< 7-bit read address. + __IO BOOL rx_dma_ok; ///< Flag indicating the completion of receive DMA. + __IO BOOL tx_dma_ok; ///< Flag indicating the completion of transmit DMA. + + i2cs_dma_callback *dma_rx_cb; ///< Callback function called when receive DMA is completed. + i2cs_dma_callback *dma_tx_cb; ///< Callback function called when transmit DMA is completed. + + i2c_interface_t interface; ///< Structure containing the function pointers for the I2C interface. + uint16_t dead_count; ///< Counter for the number of deadlocks. +}; + +/** + * @brief Creates an I2C slave instance with GPIO pins for clock and data. + * @param gpios The GPIO pins used for the I2C communication. + * @param delay_ticks The number of NOP instructions to delay the I2C communication. + * @return A pointer to the created I2C slave instance. + */ +extern i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks); + +/** + * @brief Creates an I2C slave instance with DMA support. + * @param i2c Pointer to the I2C peripheral. + * @param dma Pointer to the DMA peripheral. + * @param rxsize Size of the receive buffer. + * @param dma_rx_channel DMA channel used for receiving data. + * @param dma_rx_cb Callback function called when receive DMA is completed. + * @param txsize Size of the transmit buffer. + * @param dma_tx_channel DMA channel used for transmitting data. + * @param dma_tx_cb Callback function called when transmit DMA is completed. + * @return A pointer to the created I2C slave instance. + */ +extern i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel, + i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb); + +/** + * @brief Sets the write and read addresses for the I2C slave instance with DMA support. + * @param handle Pointer to the I2C slave instance. + * @param w_address 7-bit write address. + * @param r_address 7-bit read address. + */ +extern void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address); + +/** + * @brief Frees the resources used by the I2C slave instance. + * @param handle Pointer to the I2C slave instance. + */ +extern void i2c_free(i2c_t *handle); + +/** + * @brief Callback function called when an I2C event occurs. + * @param handle Pointer to the I2C slave instance. + */ +extern void i2c_ev_callback(i2c_t *handle); + +/** + * @brief Callback function called when an I2C DMA event occurs. + * @param handle Pointer to the I2C slave instance. + */ +extern void i2c_dma_callback(i2c_t *handle); + +#endif ///< __I2CS_H__ diff --git a/bsp/iwdgs.c b/bsp/iwdgs.c new file mode 100644 index 0000000..8b4228b --- /dev/null +++ b/bsp/iwdgs.c @@ -0,0 +1,39 @@ +#include "iwdgs.h" + +/** + * @brief 检查判断CPU复位是否是看门狗复位 + * @return {BOOL} + * @note + */ +BOOL check_watchdog_reset(void) +{ + if (LL_RCC_IsActiveFlag_IWDGRST() == SET) // cpu is reset due to iwdg + { + LL_RCC_ClearResetFlags(); // clear flag + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 调试模式冻结看门狗 + * @return {*} + * @note + */ +void debug_freeze_watchdog(void) +{ + LL_DBGMCU_APB1_GRP1_FreezePeriph(LL_DBGMCU_APB1_GRP1_IWDG_STOP); +} + +/** + * @brief 调试模式恢复看门狗 + * @return {*} + * @note + */ +void debug_unfreeze_watchdog(void) +{ + LL_DBGMCU_APB1_GRP1_UnFreezePeriph(LL_DBGMCU_APB1_GRP1_IWDG_STOP); +} diff --git a/bsp/iwdgs.h b/bsp/iwdgs.h new file mode 100644 index 0000000..9c91099 --- /dev/null +++ b/bsp/iwdgs.h @@ -0,0 +1,45 @@ +/** + * @file iwdgs.h + * @brief This file contains the declaration of the Independent Watchdog (IWDG) module. + * + * The Independent Watchdog (IWDG) is a hardware module in STM32 microcontrollers that provides a mechanism for system reset in case of software failures or malfunctions. This file declares the functions and constants related to the IWDG module. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @version 1.0 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __IWDGS_H__ +#define __IWDGS_H__ + +#include "main.h" +#include "lib.h" +/** + * @brief Reloads the watchdog counter. + * + * This macro is used to reload the watchdog counter, preventing the system from resetting. + */ +#define WATCHDOG_RESET() LL_IWDG_ReloadCounter(IWDG) + +/** + * @brief Checks if the system has been reset by the watchdog. + * + * @return BOOL Returns TRUE if the system has been reset by the watchdog, FALSE otherwise. + */ +extern BOOL check_watchdog_reset(void); + +/** + * @brief Freezes the watchdog timer for debugging purposes. + * + * This function freezes the watchdog timer, allowing for debugging without triggering a watchdog reset. + */ +extern void debug_freeze_watchdog(void); + +/** + * @brief Unfreezes the watchdog timer after debugging. + * + * This function unfreezes the watchdog timer, allowing it to resume normal operation after debugging. + */ +extern void debug_unfreeze_watchdog(void); + +#endif diff --git a/bsp/pwms.c b/bsp/pwms.c new file mode 100644 index 0000000..b8a1bea --- /dev/null +++ b/bsp/pwms.c @@ -0,0 +1 @@ +#include "pwms.h" diff --git a/bsp/pwms.h b/bsp/pwms.h new file mode 100644 index 0000000..bfce2c9 --- /dev/null +++ b/bsp/pwms.h @@ -0,0 +1,121 @@ +/** + * @file pwms.h + * @brief Header file for PWMs module. + * + * This file contains the declarations and documentation for the PWMs module. + * + * @author xxx + * @date 2023-12-27 14:44:03 + * @version 1.0 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __PWMS_H__ +#define __PWMS_H__ +#include "lib.h" + +/** + * @brief Starts the PWM for a specific channel + * @param TIMx: TIM instance + * @param CHx: Channel to be started + * @retval None + */ +#define PWM_START(TIMx, CHx) \ + do \ + { \ + LL_TIM_EnableCounter(TIMx); \ + LL_TIM_CC_EnableChannel(TIMx, CHx); \ + } while (__LINE__ == -1) + +/** + * @brief Stops the PWM for a specific channel + * @param TIMx: TIM instance + * @param CHx: Channel to be stopped + * @retval None + */ +#define PWM_STOP(TIMx, CHx) \ + do \ + { \ + LL_TIM_DisableCounter(TIMx); \ + LL_TIM_CC_DisableChannel(TIMx, CHx); \ + } while (__LINE__ == -1) + +#define PWM_GET_ARR(TIMx) LL_TIM_GetAutoReload(TIMx) +#define PWM_SET_ARR(TIMx, ARR) LL_TIM_SetAutoReload(TIMx, ARR - 1) +#define PWM_GET_PSC(TIMx) LL_TIM_GetPrescaler(TIMx) +#define PWM_SET_PSC(TIMx, PSC) LL_TIM_SetPrescaler(TIMx, PSC - 1) + +/** + * @brief Sets the PWM frequency + * @param TIMx: TIM instance + * @param CHx: Channel to be set + * @param COMPARE: Compare value + * @retval None + */ +static inline void PWM_SET_COMPARE(TIM_TypeDef *TIMx, uint32_t CHx, uint16_t COMPARE) +{ + switch (CHx) + { + case LL_TIM_CHANNEL_CH1: + LL_TIM_OC_SetCompareCH1(TIMx, COMPARE); + break; + case LL_TIM_CHANNEL_CH2: + LL_TIM_OC_SetCompareCH2(TIMx, COMPARE); + break; + case LL_TIM_CHANNEL_CH3: + LL_TIM_OC_SetCompareCH3(TIMx, COMPARE); + break; + case LL_TIM_CHANNEL_CH4: + LL_TIM_OC_SetCompareCH4(TIMx, COMPARE); + break; + default: + break; + } +} + +/** + * @brief 设置PWM占空比 + * + * 设置指定定时器TIMx的指定通道CHx的PWM占空比。 + * + * @param TIMx 定时器类型,例如TIM1、TIM2等 + * @param CHx 通道号,例如TIM_CHANNEL_1、TIM_CHANNEL_2等 + * @param DUTY 占空比,范围在0到100之间 + */ +static inline void PWM_SET_DUTY(TIM_TypeDef *TIMx, uint32_t CHx, uint16_t DUTY) +{ + PWM_SET_COMPARE(TIMx, CHx, DUTY * LL_TIM_GetAutoReload(TIMx) / 100); +} + +// 获取当前频率 +static inline uint32_t PWM_GET_FREQ(TIM_TypeDef *TIMx) +{ + return SystemCoreClock / (LL_TIM_GetPrescaler(TIMx) + 1) / (LL_TIM_GetAutoReload(TIMx) + 1); +} + +/** + * @brief 获取指定通道的PWM比较值 + * + * 根据给定的定时器指针和通道编号,获取该通道的PWM比较值。 + * + * @param TIMx 定时器指针,指向一个TIM_TypeDef结构体 + * @param CHx 通道编号,取值范围为LL_TIM_CHANNEL_CH1到LL_TIM_CHANNEL_CH4 + * + * @return 返回一个uint16_t类型的值,表示指定通道的PWM比较值。如果通道编号无效,则返回0。 + */ +static inline uint16_t PWM_GET_COMPARE(TIM_TypeDef *TIMx, uint32_t CHx) +{ + switch (CHx) + { + case LL_TIM_CHANNEL_CH1: + return LL_TIM_OC_GetCompareCH1(TIMx); + case LL_TIM_CHANNEL_CH2: + return LL_TIM_OC_GetCompareCH2(TIMx); + case LL_TIM_CHANNEL_CH3: + return LL_TIM_OC_GetCompareCH3(TIMx); + case LL_TIM_CHANNEL_CH4: + return LL_TIM_OC_GetCompareCH4(TIMx); + default: + return 0; + } +} +#endif ///< __PWMS_H__ diff --git a/bsp/readme.md b/bsp/readme.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/bsp/readme.md @@ -0,0 +1 @@ + diff --git a/bsp/spis.c b/bsp/spis.c new file mode 100644 index 0000000..bffe1da --- /dev/null +++ b/bsp/spis.c @@ -0,0 +1,819 @@ +#include "spis.h" +#include "delay.h" + +#define SPI_TIMEOUT 0xffffffff + +#define CMD_RDSR 0x05 /*!< Read Status Register instruction */ +#define CMD_WRSR 0x01 /*!< Write Status Register instruction */ +#define CMD_WREN 0x06 /*!< Write enable instruction */ +#define CMD_WRDI 0x04 /*!< Write disable instruction */ +#define CMD_READ 0x03 /*!< Read from Memory instruction */ +#define CMD_WRITE 0x02 /*!< Write to Memory instruction */ +#define DUMMY_BYTE 0xA5 ///< 虚拟字节 + +static inline void spi_delay(spi_t *handle); // 延时函数 + +static inline void spi_rdy_high(spi_t *handle); // RDY高电平 +static inline void spi_rdy_low(spi_t *handle); // RDY低电平 +static inline void spi_cs_high(spi_t *handle); // CS高电平 +static inline void spi_cs_low(spi_t *handle); // CS低电平 +static inline void spi_mosi_high(spi_t *handle); // MOSI高电平 +static inline void spi_mosi_low(spi_t *handle); // MOSI低电平 +static inline void spi_sck_high(spi_t *handle); // SCK高电平 +static inline void spi_sck_low(spi_t *handle); // SCK低电平 +static inline uint8_t spi_miso_read(spi_t *handle); // 读取MISO电平 + +static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data); // 读写一个字节 +static void spi_reset(spi_t *handle); // 复位 +static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 写数据 +static BOOL spi_read(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 读数据 +static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写寄存器 +static uint8_t spi_read_reg(spi_t *handle, uint8_t reg); // 读寄存器 + +static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi); // 硬件SPI +static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb, + uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); // DMA SPI +static void _spi_dma_callback(spi_t *handle); // DMA发送完成回调 +static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length); // DMA发送数据 +static uint8_t _read_drdy(spi_t *handle); // 读取DRDY电平 +static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 写多个寄存器 +static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 读多个寄存器 +static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写单个寄存器 +static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg); // 读单个寄存器 +static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd); // 写命令 +static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len); // 写数据 + +/** + * @brief 创建一个SPI总线设备 + * @param {spi_type_e} spi_type SPI总线的类型 + * @param {spi_gpio_group_t} gpios SPI总线的GPIO配置 + * @param {uint16_t} delay_ticks SPI总线的延时参数 + * @return {*} 创建的SPI总线设备句柄 + * @note: 该函数用于创建一个SPI总线设备。它首先断言spi_type在有效的范围内,然后创建一个spi_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。接着,根据spi_type的值,设置不同的接口函数。最后,返回handle结构体。 + */ +spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks) +{ + DBG_ASSERT(spi_type < SPI_TYPE_MAX __DBG_LINE); + spi_t *handle = (spi_t *)osel_mem_alloc(sizeof(spi_t)); + osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(spi_gpio_group_t)); + handle->delay_ticks = delay_ticks; + handle->spi_type = spi_type; + handle->simualte_gpio = TRUE; + handle->interface.hardware_enable = _hardware_enable; + handle->interface.dma_enable = _dma_enable; + handle->interface.spi_dma_send = _spi_dma_send; + handle->interface.spi_dma_callback = _spi_dma_callback; + if (spi_type == SPI_TYPE_NORMAL) + { + handle->interface.u.normal.write_reg = _spi_write_reg; + handle->interface.u.normal.read_reg = _spi_read_reg; + handle->interface.u.normal.write_regs = _write_regs; + handle->interface.u.normal.read_regs = _read_regs; + handle->interface.u.normal.read_drdy = _read_drdy; + + handle->interface.u.normal.spi_send = spi_read_write_byte; + handle->interface.u.normal.spi_reset = spi_reset; + handle->interface.u.normal.spi_read = spi_read; + handle->interface.u.normal.spi_write = spi_write; + handle->interface.u.normal.spi_write_reg = spi_write_reg; + handle->interface.u.normal.spi_read_reg = spi_read_reg; + + handle->cfg.cmd_rdsr = CMD_RDSR; + handle->cfg.cmd_wrsr = CMD_WRSR; + handle->cfg.cmd_wren = CMD_WREN; + handle->cfg.cmd_wrdi = CMD_WRDI; + handle->cfg.cmd_read = CMD_READ; + handle->cfg.cmd_write = CMD_WRITE; + handle->cfg.dummy_byte = DUMMY_BYTE; + handle->cfg.continuous_write = FALSE; + } + else if (spi_type == SPI_TYPE_LCD) + { + handle->interface.u.lcd.write_cmd = _spi_write_cmd; + handle->interface.u.lcd.write_data = _spi_write_data; + } + else + { + DBG_ASSERT(FALSE __DBG_LINE); + } + return handle; +} + +void spi_free(spi_t *handle) +{ + if (handle != NULL) + { + gpio_free(handle->gpios.cs); + gpio_free(handle->gpios.rdy); + gpio_free(handle->gpios.rst); + gpio_free(handle->gpios.mosi); + gpio_free(handle->gpios.miso); + gpio_free(handle->gpios.sck); + + osel_mem_free(handle); + } +} + +/** + * @brief 硬件SPI模式 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {SPI_TypeDef} *spi SPI总线的寄存器结构体指针 + * @return {*} 无 + * @note: 该函数用于设置SPI总线的硬件模式。它首先断言handle不为空,然后断言spix不为空。接着,将handle的simulate_gpio设置为FALSE,并将spix的地址赋值给handle->spix。最后,调用SPI_ENABLE函数启用SPI总线。 + */ +static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(spi != NULL __DBG_LINE); + handle->simualte_gpio = FALSE; + handle->spi = spi; + SPI_ENABLE(spi); +} + +static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb, + uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(dma != NULL __DBG_LINE); + DBG_ASSERT(handle->spi != NULL __DBG_LINE); + handle->dma = dma; + handle->dma_rx_channel = dma_rx_channel; + handle->dma_tx_channel = dma_tx_channel; + handle->rx_dma_ok = TRUE; + handle->tx_dma_ok = TRUE; + if (dma_rx_cb != NULL) + { + handle->dma_rx_cb = dma_rx_cb; + } + if (dma_tx_cb != NULL) + { + handle->dma_tx_cb = dma_tx_cb; + } + + LL_DMA_SetPeriphAddress(dma, dma_tx_channel, LL_SPI_DMA_GetRegAddr(handle->spi)); + LL_DMA_ClearFlag_GI1(dma); + LL_DMA_ClearFlag_TC1(dma); + LL_DMA_EnableIT_TC(dma, dma_tx_channel); + LL_SPI_EnableDMAReq_TX(handle->spi); + LL_SPI_Enable(handle->spi); +} + +static void _spi_dma_callback(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + if (handle->dma_tx_cb != NULL) + { + handle->dma_tx_cb(handle); + } + + if (handle->dma_rx_cb != NULL) + { + handle->dma_rx_cb(handle); + } +} + +static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length) +{ + handle->tx_dma_ok = FALSE; + LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel); + + LL_DMA_SetMemoryAddress(handle->dma, handle->dma_tx_channel, (uint32_t)data); + LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, length); + + LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel); + + uint16_t i = 0; + while (handle->tx_dma_ok == FALSE) + { + i++; + if (i > 50000) + { + __NOP(); + break; + } + } + + return handle->tx_dma_ok == TRUE ? TRUE : FALSE; +} + +/** + * @brief 读取数据准备好信号 + * @param {spi_t} *handle SPI总线设备句柄 + * @return {uint8_t} 数据准备好信号的值 + * @note: 该函数用于读取SPI总线的数据准备好信号。它首先断言handle不为空,然后返回handle的gpios.rdy的读取结果。 + */ +static uint8_t _read_drdy(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + return handle->gpios.rdy->read(*handle->gpios.rdy); +} + +/** + * @brief 写入单个寄存器 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} reg 寄存器号 + * @param {uint8_t} data 寄存器值 + * @return {*} 操作结果 + * @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后断言data不为空。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。 + */ +static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len) +{ + uint8_t status = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + DBG_ASSERT(len != 0 __DBG_LINE); + spi_cs_low(handle); + status = spi_read_write_byte(handle, reg); // 发送寄存器号 + while (len--) + { + spi_read_write_byte(handle, *data); // 写入寄存器的值 + data++; + } + spi_cs_high(handle); + + return status; +} + +/** + * @brief 读取多个寄存器 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} reg 寄存器号 + * @param {uint8_t} *data 寄存器值的指针 + * @param {uint8_t} len 寄存器值的个数 + * @return {uint8_t} 操作结果 + * @note: 该函数用于读取SPI总线的多个寄存器。它首先断言handle不为空,然后断言data不为空,接着断言len大于0。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号,然后循环读取多个寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。 + */ +static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len) +{ + uint8_t status = 0; + + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + DBG_ASSERT(len != 0 __DBG_LINE); + + spi_cs_low(handle); + status = spi_read_write_byte(handle, reg); + for (uint8_t i = 0; i < len; i++) + { + data[i] = spi_read_write_byte(handle, 0xff); // 读出数据 + } + spi_cs_high(handle); + + return status; +} + +/** + * @brief 写入单个寄存器 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} reg 寄存器号 + * @param {uint8_t} data 寄存器值 + * @return {*} 操作结果 + * @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。 + */ +static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data) +{ + uint8_t status = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + + spi_cs_low(handle); + status = spi_read_write_byte(handle, reg); // 发送寄存器号 + spi_read_write_byte(handle, data); // 写入寄存器的值 + + spi_cs_high(handle); + + return status; +} + +/** + * @brief 读取单个寄存器 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} reg 寄存器号 + * @return {*} 寄存器值 + * @note: 该函数用于读取SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号,接着读取寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回寄存器值。 + */ +static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg) +{ + uint8_t reg_val = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + + spi_cs_low(handle); + spi_read_write_byte(handle, reg); // 发送寄存器号 + reg_val = spi_read_write_byte(handle, 0); + spi_cs_high(handle); + return reg_val; +} + +/** + * @brief 写入命令 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} cmd 命令 + * @return {*} 操作结果 + * @note: 该函数用于写入SPI总线的命令。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为低电平,发送命令,最后将SPI总线的CS引脚设置为高电平,返回操作结果。 + */ +static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd) +{ + uint8_t status = 0; + + DBG_ASSERT(handle != NULL __DBG_LINE); + spi_rdy_low(handle); + spi_cs_low(handle); + status = spi_read_write_byte(handle, cmd); // 发送命令 + spi_cs_high(handle); + return status; +} + +/** + * @brief 写入数据 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} *data 要写入的数据的指针 + * @param {uint16_t} len 要写入的数据的长度 + * @return {*} 操作结果 + * @note: 该函数用于写入SPI总线的数据。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为高电平,发送数据,最后将SPI总线的CS引脚设置为高电平,返回操作结果。 + */ +static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len) +{ + uint8_t status = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + spi_rdy_high(handle); + spi_cs_low(handle); + for (uint16_t i = 0; i < len; i++) + { + status = spi_read_write_byte(handle, *data); + data++; + } + spi_cs_high(handle); + return status; +} + +/** + * @brief SPI延时函数 + * @param {spi_t} *handle SPI总线设备句柄 + * @return {*} 无 + * @note: 该函数用于SPI总线的延时。它首先断言handle不为空,然后根据handle的delay_ticks的值,循环执行NOP指令。 + */ +static inline void spi_delay(spi_t *handle) +{ + uint16_t count = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + count = handle->delay_ticks; + if (count > 0) + { + while (count--) + { + __NOP(); + } + } +} + +static BOOL spi_wait_flag(spi_t *handle, uint32_t flag, uint32_t timeout) +{ + uint32_t i = 0; + DBG_ASSERT(handle != NULL __DBG_LINE); + if (flag == LL_SPI_SR_TXE) + { + while (LL_SPI_IsActiveFlag_TXE(handle->spi) == 0) + { + if (i++ > timeout) + { + return FALSE; // 超时 + } + else + { + __NOP(); + } + } + } + else if (flag == LL_SPI_SR_RXNE) + { + while (LL_SPI_IsActiveFlag_RXNE(handle->spi) == 0) + { + if (i++ > timeout) + { + return FALSE; // 超时 + } + else + { + __NOP(); + } + } + } + else if (flag == LL_SPI_SR_BSY) + { + while (LL_SPI_IsActiveFlag_BSY(handle->spi) == 0) + { + if (i++ > timeout) + { + return FALSE; // 超时 + } + else + { + __NOP(); + } + } + } + else + { + DBG_ASSERT(FALSE __DBG_LINE); + } + + return TRUE; // 成功 +} + +/** + * @brief 读写一个字节 + * @param {spi_t} *handle SPI总线设备句柄 + * @param {uint8_t} tx_data 要写入的数据 + * @return {*} 读取到的数据 + * @note: 该函数用于SPI总线的读写一个字节。它首先断言handle不为空,然后根据handle的simulate_gpio的值,选择模拟SPI或硬件SPI。最后,返回读取到的数据。 + */ +static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data) +{ + uint8_t bit_ctr; + uint8_t rdata = 0xff; + DBG_ASSERT(handle != NULL __DBG_LINE); + if (handle->simualte_gpio == TRUE) + { + // 模拟SPI + for (bit_ctr = 0; bit_ctr < 8; bit_ctr++) + { + spi_sck_low(handle); + spi_delay(handle); + + if (tx_data & 0x80) + spi_mosi_high(handle); + else + spi_mosi_low(handle); + + spi_delay(handle); + spi_sck_high(handle); + spi_delay(handle); + tx_data = (tx_data << 1); + rdata = rdata << 1; + + if (NULL != handle->gpios.miso->port) + { + if (spi_miso_read(handle) == 1) + rdata += 0x01; + } + } + return (rdata); + } + else + { + if (spi_wait_flag(handle, LL_SPI_SR_TXE, SPI_TIMEOUT) == FALSE) + { + return 0xff; + } + LL_SPI_TransmitData8(handle->spi, tx_data); + + if (spi_wait_flag(handle, LL_SPI_SR_RXNE, SPI_TIMEOUT) == FALSE) + { + return 0xff; + } + rdata = LL_SPI_ReceiveData8(handle->spi); + return rdata; + } +} + +static void _write_enable(spi_t *handle) +{ + handle->gpios.cs->reset(*handle->gpios.cs); + handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wren); + handle->gpios.cs->set(*handle->gpios.cs); +} + +static void _write_disable(spi_t *handle) +{ + handle->gpios.cs->reset(*handle->gpios.cs); + handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wrdi); + handle->gpios.cs->set(*handle->gpios.cs); +} + +static uint8_t _read_status(spi_t *handle) +{ + uint8_t data; + handle->gpios.cs->reset(*handle->gpios.cs); + handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_rdsr); + data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte); + handle->gpios.cs->set(*handle->gpios.cs); + return data; +} + +static void _ready(spi_t *handle) +{ + uint16_t count = 0; + while (_read_status(handle) & 0x01) + { + if (count++ > 20000) + { + break; + } + else + { + __NOP(); + } + } +} + +static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = TRUE; + uint8_t cnt = 0; // 返回值检查 + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + DBG_ASSERT(length > 0 __DBG_LINE); + DBG_ASSERT(handle->cfg.page_size > 0 __DBG_LINE); + uint32_t page_size = handle->cfg.page_size; + _write_enable(handle); // 写入使能命令 + handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 + + cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令 + if (cnt == 0) + { + return FALSE; + } + + if (handle->cfg.address_bytes == 2) + { + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址 + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址 + if (cnt == 0) + { + return FALSE; + } + } + else + { + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr); + if (cnt == 0) + { + return FALSE; + } + } + + while (length--) + { + cnt = handle->interface.u.normal.spi_send(handle, *data); // 发送一个字节数据 + if (cnt == 0) + { + return FALSE; + } + data++; + if (handle->cfg.continuous_write == FALSE) + { + write_addr++; + if (((write_addr % page_size) == 0) && (length > 0)) + { + // 一页写完 + handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 + _ready(handle); + + _write_enable(handle); // 写入使能命令 + handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 + + cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令 + if (cnt == 0) + { + return FALSE; + } + if (handle->cfg.address_bytes == 2) + { + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址 + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址 + if (cnt == 0) + { + return FALSE; + } + } + else + { + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, write_addr); + if (cnt == 0) + { + return FALSE; + } + } + } + } + } + handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 + + _ready(handle); + _write_disable(handle); + return ret; +} + +static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t value) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + _write_enable(handle); // 写入使能命令 + handle->interface.u.normal.write_reg(handle, reg, value); + _ready(handle); + _write_disable(handle); // 写入禁止命令 +} + +static uint8_t spi_read_reg(spi_t *handle, uint8_t reg) +{ + uint8_t data; + handle->gpios.cs->reset(*handle->gpios.cs); + handle->interface.u.normal.spi_send(handle, reg); + data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte); + handle->gpios.cs->set(*handle->gpios.cs); + return data; +} + +static BOOL spi_read(spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = TRUE; + uint8_t cnt = 0; // 返回值检查 + DBG_ASSERT(handle != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + DBG_ASSERT(length > 0 __DBG_LINE); + handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 + + cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_read); // 发送读取命令 + if (cnt == 0) + { + return FALSE; + } + if (handle->cfg.address_bytes == 2) + { + cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8); // 发送高位地址 + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, read_addr); // 发送低位地址 + if (cnt == 0) + { + return FALSE; + } + } + else if (handle->cfg.address_bytes == 3) + { + cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 16); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8); + if (cnt == 0) + { + return FALSE; + } + cnt = handle->interface.u.normal.spi_send(handle, read_addr); + if (cnt == 0) + { + return FALSE; + } + } + else + { + DBG_ASSERT(FALSE __DBG_LINE); + } + for (uint16_t i = 0; i < length; i++) // 循环读取数据 + { + data[i] = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte); // 发送空字节,读取实际数据 + } + handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 + + return ret; +} + +static void spi_reset(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.cs->reset(*handle->gpios.cs); + delay_tick(10); + handle->gpios.cs->set(*handle->gpios.cs); + delay_tick(10); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为高 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_rdy_high(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.rdy->set(*handle->gpios.rdy); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为低 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_rdy_low(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.rdy->reset(*handle->gpios.rdy); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为高 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_cs_high(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.cs->set(*handle->gpios.cs); + spi_delay(handle); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为低 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_cs_low(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.cs->reset(*handle->gpios.cs); + spi_delay(handle); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_mosi_high(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.mosi->set(*handle->gpios.mosi); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的输出(MOSI)引脚设置为低 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_mosi_low(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.mosi->reset(*handle->gpios.mosi); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_sck_high(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.sck->set(*handle->gpios.sck); +} + +/** + * @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为低 + * @param {spi_id_e} id + * @return {*} + */ +static inline void spi_sck_low(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + handle->gpios.sck->reset(*handle->gpios.sck); +} + +/** + * @brief 用于读取SPI(串行外围接口)设备的输入(MISO)引脚的电平 + * @param {spi_id_e} id + * @return {*} + */ +static inline uint8_t spi_miso_read(spi_t *handle) +{ + DBG_ASSERT(handle != NULL __DBG_LINE); + return handle->gpios.miso->read(*handle->gpios.miso); +} diff --git a/bsp/spis.h b/bsp/spis.h new file mode 100644 index 0000000..fa18999 --- /dev/null +++ b/bsp/spis.h @@ -0,0 +1,166 @@ +/** + * @file spis.h + * @brief SPI驱动, 用于SPI设备的读写操作 + * + * This file contains the SPI driver used for reading and writing operations on SPI devices. + * + * @date 2023-08-01 + * @version 1.0 + * + * @note This file is part of the STM32 controller-v2 project. + * + */ + +#ifndef __SPIS_H__ +#define __SPIS_H__ + +#include "lib.h" +#include "gpios.h" + +#define SPI_ENABLE(SPIX) LL_SPI_Enable(SPIX) + +typedef struct GPIO gpio_t; +typedef struct SPIS spi_t; +typedef void spis_dma_callback(spi_t *handle); + +/** + * @brief SPI type enumeration + */ +typedef enum +{ + SPI_TYPE_NORMAL = 0, ///< SPI1:NORMAL + SPI_TYPE_LCD, ///< SPI2:LCD + SPI_TYPE_MAX, +} spi_type_e; + +/** + * @brief SPI GPIO group structure + */ +typedef struct +{ + gpio_t *mosi; ///< MOSI + gpio_t *miso; ///< MISO + gpio_t *sck; ///< SCK + gpio_t *cs; ///< CS + gpio_t *rst; ///< RST + gpio_t *rdy; ///< DRDY +} spi_gpio_group_t; + +/** + * @brief SPI normal interface structure + */ +typedef struct +{ + uint8_t (*write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI + uint8_t (*read_reg)(spi_t *handle, uint8_t reg); ///< Read the value of a single register via SPI + uint8_t (*read_drdy)(spi_t *handle); ///< Get the value of the SPI DRDY pin + uint8_t (*write_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Write multiple registers via SPI + uint8_t (*read_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Read multiple registers via SPI + uint8_t (*spi_send)(spi_t *handle, uint8_t data); ///< Send data via SPI + void (*spi_reset)(spi_t *handle); ///< Reset SPI + + BOOL(*spi_write) + (spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); ///< Write data via SPI + + BOOL(*spi_read) + (spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length); ///< Read data via SPI + + void (*spi_write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI + uint8_t (*spi_read_reg)(spi_t *handle, uint8_t reg); ///< Read a single register via SPI +} spi_normal_interface_t; + +/** + * @brief SPI LCD interface structure + */ +typedef struct +{ + uint8_t (*write_cmd)(spi_t *handle, uint8_t cmd); ///< Write a command via SPI + uint8_t (*write_data)(spi_t *handle, uint8_t *data, uint16_t len); ///< Write data via SPI +} spi_lcd_interface_t; + +/** + * @brief SPI interface structure + */ +typedef struct +{ + union + { + spi_normal_interface_t normal; + spi_lcd_interface_t lcd; + } u; + void (*hardware_enable)(spi_t *handle, SPI_TypeDef *spi); ///< Enable hardware SPI + void (*dma_enable)(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb, + uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); ///< Enable DMA SPI + void (*spi_dma_callback)(spi_t *spi); ///< DMA send completion callback + BOOL(*spi_dma_send) + (spi_t *handle, uint8_t *data, uint16_t length); ///< DMA send +} spi_interface_t; + +/** + * @brief SPI structure + */ +typedef struct SPIS spi_t; + +/** + * @brief SPI DMA callback function + */ +typedef void spis_dma_callback(spi_t *handle); + +typedef struct +{ + // CMD + uint8_t cmd_rdsr; ///< Read Status Register instruction + uint8_t cmd_wrsr; ///< Write Status Register instruction + uint8_t cmd_wren; ///< Write enable instruction + uint8_t cmd_wrdi; ///< Write disable instruction + uint8_t cmd_read; ///< Read from Memory instruction + uint8_t cmd_write; ///< Write to Memory instruction + + uint8_t dummy_byte; ///< Dummy byte + + uint8_t address_bytes; + uint32_t page_size; + uint32_t total_size; + uint8_t ticks; ///< Delay in NOP ticks + BOOL continuous_write; ///< Continuous write +} spi_normal_config_t; + +struct SPIS +{ + spi_type_e spi_type; ///< SPI type + uint16_t delay_ticks; ///< Delay in NOP ticks + spi_gpio_group_t gpios; ///< SPI GPIOs + spi_interface_t interface; ///< SPI interface + SPI_TypeDef *spi; ///< SPI peripheral + BOOL simualte_gpio; ///< Simulate GPIO + spi_normal_config_t cfg; ///< Normal SPI configuration + ///< DMA + DMA_TypeDef *dma; ///< External setting + uint32_t dma_rx_channel; ///< External setting + uint32_t dma_tx_channel; ///< External setting + __IO BOOL rx_dma_ok; + __IO BOOL tx_dma_ok; + spis_dma_callback *dma_rx_cb; ///< DMA receive callback function + spis_dma_callback *dma_tx_cb; ///< DMA send callback function + + void *params; ///< 扩展参数 +}; + +/** + * @brief Create a new SPI instance + * + * @param spi_type The type of SPI + * @param gpios The SPI GPIO group + * @param delay_ticks The delay in NOP ticks + * @return spi_t* The created SPI instance + */ +extern spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks); + +/** + * @brief Free the SPI instance + * + * @param spi The SPI instance to free + */ +extern void spi_free(spi_t *spi); + +#endif ///< __SPIS_H__ diff --git a/bsp/tims.c b/bsp/tims.c new file mode 100644 index 0000000..03d544d --- /dev/null +++ b/bsp/tims.c @@ -0,0 +1 @@ +#include "tims.h" diff --git a/bsp/tims.h b/bsp/tims.h new file mode 100644 index 0000000..71d3c63 --- /dev/null +++ b/bsp/tims.h @@ -0,0 +1,116 @@ +/** + * @file tims.h + * @brief Header file for TIMS module. + * + * This file contains the declarations and definitions for the TIMS module. + * TIMS stands for Timer System and provides functionality related to timers. + * + * @author xxx + * @date 2024-01-16 22:23:43 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __TIMS_H__ +#define __TIMS_H__ +#include "lib.h" +/** +Timer overflow time calculation formula +Tout = ((ARR + 1)*(PSC + 1)) / Tclk +Given Tclk as 84MHz, we need Tout to be 200ms or 200000us. Let's assume PSC is 839, substituting it into the formula gives ARR = 19999. +With these calculated values, both ARR and PSC are within the range of 0 to 65535, so we can use this parameter set. + */ + +/** + * @brief Enables the specified TIMx. + * @param TIMx TIM instance. + */ +#define ENABLE_TIM(TIMx) \ + do \ + { \ + LL_TIM_EnableCounter(TIMx); \ + LL_TIM_EnableIT_UPDATE(TIMx); \ + } while (__LINE__ == -1); + +/** + * @brief Checks if the specified TIMx is enabled. + * @param TIMx TIM instance. + * @retval The new state of TIMx (1 or 0). + */ +#define IS_ENABLE_TIM(TIMx) LL_TIM_IsEnabledIT_UPDATE(TIMx) + +/** + * @brief Disables the specified TIMx. + * @param TIMx TIM instance. + */ +#define DISABLE_TIM(TIMx) \ + do \ + { \ + LL_TIM_DisableCounter(TIMx); \ + LL_TIM_DisableIT_UPDATE(TIMx); \ + } while (__LINE__ == -1); + +#define ENABLE_TIM_ARR_RELOAD(TIMx) LL_TIM_EnableARRPreload(TIMx) +#define DISABLE_TIM_ARR_RELOAD(TIMx) LL_TIM_DisableARRPreload(TIMx) + +/** + * @brief Checks if the specified TIMx interrupt flag is set. + * @param TIMx TIM instance. + * @retval The new state of the specified TIMx interrupt flag (1 or 0). + */ +#define IS_TIM_IT_FLAG(TIMx) (LL_TIM_IsActiveFlag_UPDATE(TIMx) == 1) + +/** + * @brief TIM interrupt handler. + * @param TIMx TIM instance. + */ +#define TIM_IRQ_HANDLER(TIMx) \ + do \ + { \ + if (LL_TIM_IsActiveFlag_CC1(TIMx) == SET) \ + { \ + if (LL_TIM_IsEnabledIT_CC1(TIMx) == SET) \ + { \ + LL_TIM_ClearFlag_CC1(TIMx); \ + } \ + } \ + if (LL_TIM_IsActiveFlag_CC2(TIMx) == SET) \ + { \ + if (LL_TIM_IsEnabledIT_CC2(TIMx) == SET) \ + { \ + LL_TIM_ClearFlag_CC2(TIMx); \ + } \ + } \ + if (LL_TIM_IsActiveFlag_CC3(TIMx) == SET) \ + { \ + if (LL_TIM_IsEnabledIT_CC3(TIMx) == SET) \ + { \ + LL_TIM_ClearFlag_CC3(TIMx); \ + } \ + } \ + if (LL_TIM_IsActiveFlag_CC4(TIMx) == SET) \ + { \ + if (LL_TIM_IsEnabledIT_CC4(TIMx) == SET) \ + { \ + LL_TIM_ClearFlag_CC4(TIMx); \ + } \ + } \ + if (LL_TIM_IsActiveFlag_UPDATE(TIMx) == SET) \ + { \ + if (LL_TIM_IsEnabledIT_UPDATE(TIMx) == SET) \ + { \ + LL_TIM_ClearFlag_UPDATE(TIMx); \ + } \ + } \ + } while (__LINE__ == -1) + +/** + * @brief 获取定时器周期时间(单位:毫秒) + * + * 获取指定定时器TIMx的周期时间,以秒为单位。 + * + * @param TIMx 定时器指针,指向需要查询的定时器 + * + * @return 返回定时器周期时间(单位:毫秒) + */ +#define TIM_CYCLE(TIMx) ((LL_TIM_GetAutoReload(TIMx) + 1) * 0.1) + +#endif ///< __TIMS_H__ diff --git a/bsp/uarts.c b/bsp/uarts.c new file mode 100644 index 0000000..7868a74 --- /dev/null +++ b/bsp/uarts.c @@ -0,0 +1,486 @@ +/* + * @Author: + * @Date: 2023-07-31 11:47:35 + * @LastEditors: xxx + * @LastEditTime: 2023-08-25 15:30:33 + * @Description: LL库的串口驱动 + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#include "uarts.h" +#include "dma.h" + +// 清理接收中断错误标志 +static void uart_clear_error(uart_t *uart) +{ + if (uart == NULL) + { + return; + } + uart->rx_error_count = 0; + if (uart->rx_err_en == TRUE && uart->rx_error != NULL) + { + osel_memset((uint8_t *)uart->rx_error, 0, sizeof(uarts_interupt_error_t) * uart->rxsize); + } +} +/** + * @brief 创建一个UART设备 + * @param {USART_TypeDef} *huart USART总线设备句柄 + * @param {BOOL} rx_dma_en 接收DMA使能标志 + * @param {uint16_t} rxsize 接收缓冲区大小 + * @param {rx_interrupt_cb_t} rx_cb 接收中断回调函数 + * @param {BOOL} tx_dma_en 发送DMA使能标志 + * @param {uint16_t} txsize 发送缓冲区大小 + * @param {tx_complete_cb_t} tx_complete_cb 发送完成回调函数 + * @return {*} 创建的UART设备指针 + * @note: 该函数用于创建一个UART设备。它首先断言huart不为空,然后分配内存用于接收缓冲区和发送缓冲区,并设置相关标志。最后,返回创建的UART设备指针。 + */ +uart_t *uart_create(USART_TypeDef *huart, BOOL rx_dma_en, uint16_t rxsize, rx_interupt_cb_t rx_cb, + BOOL tx_dma_en, uint16_t txsize, tx_complete_cb_t tx_complete_cb) +{ + DBG_ASSERT(huart != NULL __DBG_LINE); + // 分配内存 + uart_t *uart = (uart_t *)osel_mem_alloc(sizeof(uart_t)); + DBG_ASSERT(uart != NULL __DBG_LINE); + + uart->rx_interupt_timeout = TRUE; // 接收超时标志 + uart->rx_interupt_cnt = 0; // 接收中断计数 + + // 设置接收回调函数 + uart->rx_interupt_cb = rx_cb; + // 设置接收数据大小 + uart->rxsize = rxsize; + + // 设置发送完成回调函数 + uart->tx_complete_cb = tx_complete_cb; + // 设置发送数据大小 + uart->txsize = txsize; + // 如果接收大小大于0,则分配内存 + if (rxsize > 0) + { + uart->rxbuf = (uint8_t *)osel_mem_alloc(rxsize); + DBG_ASSERT(uart->rxbuf != NULL __DBG_LINE); + } + + // 如果发送大小大于0,则分配内存 + if (txsize > 0) + { + uart->txbuf = (uint8_t *)osel_mem_alloc(txsize); + DBG_ASSERT(uart->txbuf != NULL __DBG_LINE); + } + // 设置接收DMA禁用 + uart->rx_dma_en = rx_dma_en; + // 设置发送DMA禁用 + uart->tx_dma_en = tx_dma_en; + + // 设置huart + uart->huart = huart; + + // 返回uart + return uart; +} + +/** + * @brief 使能UART接收 + * @param {uart_t} *uart UART设备句柄 + * @param {BOOL} rx_err_en 接收错误使能标志,只能用于串口中断模式下使用 + * @return {*} 操作结果 + * @note: 该函数用于使能UART设备的接收功能。它首先检查UART设备的接收DMA使能标志,然后禁用接收中断,配置RX DMA并启用RX DMA通道。最后,检查UART设备的发送DMA使能标志,配置TX DMA并启用TX DMA通道。 + */ +void uart_recv_en(uart_t *uart, BOOL rx_err_en) +{ + if (FALSE == uart->rx_dma_en) + { + uart->rx_err_en = rx_err_en; + LL_USART_EnableIT_RXNE(uart->huart); // 使用接收中断处理 + } + else + { + uart->rx_err_en = FALSE; + LL_USART_ClearFlag_IDLE(uart->huart); + + // 配置RX DMA + LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); + LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel); + + // 配置RX DMA + LL_DMA_SetPeriphAddress(uart->dma, uart->dma_rx_channel, LL_USART_DMA_GetRegAddr(uart->huart)); + LL_DMA_SetMemoryAddress(uart->dma, uart->dma_rx_channel, (uint32_t)uart->rxbuf); + LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize); + LL_DMA_EnableIT_TC(uart->dma, uart->dma_rx_channel); + LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel); + LL_USART_EnableDMAReq_RX(uart->huart); + LL_USART_EnableIT_IDLE(uart->huart); + + // 配置TX DMA + LL_DMA_SetPeriphAddress(uart->dma, uart->dma_tx_channel, LL_USART_DMA_GetRegAddr(uart->huart)); + // 配置内存地址 + LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf); + LL_DMA_EnableIT_TC(uart->dma, uart->dma_tx_channel); + LL_USART_EnableDMAReq_TX(uart->huart); + + uart->tx_dma_ok = TRUE; + } + + if (uart->rx_err_en == TRUE) + { + if (uart->rx_error == NULL) + { + uart->rx_error = (uarts_interupt_error_t *)osel_mem_alloc(sizeof(uarts_interupt_error_t) * uart->rxsize); + DBG_ASSERT(uart->rx_error != NULL __DBG_LINE); + } + + LL_USART_EnableIT_PE(uart->huart); // 使能奇偶校验错误中断 + // 使能帧错误中断 + // 使能帧错误中断 + // 使能溢出错误中断 + // LL_USART_EnableIT_ERROR 可以使能上面3个中断 + /** + * When set, Error Interrupt Enable Bit is enabling interrupt generation in case of a framing + * error, overrun error or noise flag (FE=1 or ORE=1 or NF=1 in the USARTx_ISR register). + */ + LL_USART_EnableIT_ERROR(uart->huart); + } +} + +/** + * @brief 释放UART设备资源 + * @param {uart_t} *uart UART设备句柄 + * @return {*} 操作结果 + * @note: 该函数用于释放UART设备的接收缓冲区、发送缓冲区和UART设备本身。 + */ +void uart_free(uart_t *uart) +{ + if (uart != NULL) + { + if (uart->rxbuf != NULL) + { + osel_mem_free(uart->rxbuf); + } + if (uart->rx_error != NULL) + { + osel_mem_free(uart->rx_error); + } + if (uart->txbuf != NULL) + { + osel_mem_free(uart->txbuf); + } + osel_mem_free(uart); + } +} + +/** + * @brief 设置波特率 + * @param {uart_t} *uart + * @param {uint32_t} baudrate 波特率 + * @return {*} + * @note 可以在超频后串口数据重新通讯 + */ +void uart_set_baudrate(USART_TypeDef *uart, uint32_t baudrate) +{ + LL_USART_SetBaudRate(uart, SystemCoreClock, LL_USART_OVERSAMPLING_16); +} + +/** + * @brief 发送数据 + * @param {uart_t} *uart UART设备句柄 + * @param {uint8_t} *data 要发送的数据 + * @param {uint16_t} len 要发送的数据长度 + * @return {*} 操作结果 + * @note: 该函数用于发送数据。首先检查UART设备的发送DMA使能标志,然后禁用发送中断,配置TX DMA并启用TX DMA通道。最后,发送数据直到发送缓冲区满或发送中断发生。 + */ +void uart_send_data(uart_t *uart, uint8_t *data, uint16_t len) +{ + DBG_ASSERT(uart != NULL __DBG_LINE); + DBG_ASSERT(data != NULL __DBG_LINE); + DBG_ASSERT(len > 0 __DBG_LINE); + uint16_t count = 0; + if (TRUE == uart->tx_dma_en) + { + uart->tx_dma_ok = FALSE; + osel_memcpy(uart->txbuf, data, len); // 拷贝数据到发送缓冲区 + LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); + // 配置 DMA 源地址 + LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf); + // 配置数据长度 + LL_DMA_SetDataLength(uart->dma, uart->dma_tx_channel, len); + // 使能DMA STREAM 也就是发送数据 + LL_DMA_EnableChannel(uart->dma, uart->dma_tx_channel); + // 等待DMA发送完成 + while (uart->tx_dma_ok == FALSE) + { + if (count++ >= 2000) + { + return; + } + } + } + else + { + count = 0; + for (uint16_t i = 0; i < len; i++) + { + count = 0; + LL_USART_TransmitData8(uart->huart, data[i]); + while (!LL_USART_IsActiveFlag_TXE(uart->huart)) + { + if (count++ >= 0xFE) + { + count = 0; + continue; + } + } + } + while (!LL_USART_IsActiveFlag_TC(uart->huart)) + { + if (count++ >= 0xFE) + { + count = 0; + continue; + } + } + if (uart->tx_complete_cb != NULL) + { + uart->tx_complete_cb(); + } + LL_USART_ClearFlag_TC(uart->huart); + } +} + +/** + * @brief UART接收超时定时器 + * + * 当UART接收超时定时器触发时,调用此函数处理UART接收超时事件。 + * + * @param uart UART对象指针 + */ +void uart_rx_timeout_timer(uart_t *uart) +{ + // DBG_ASSERT(uart != NULL __DBG_LINE); + if (uart == NULL) + { + return; + } + if (uart->rx_dma_en == FALSE && uart->rx_interupt_timeout == FALSE) // 中断方式 + { + if (uart->rx_interupt_cnt++ == RX_TIMEOUT_MSEC) + { + uart->rx_interupt_timeout = TRUE; + if (uart->rx_interupt_cb != NULL && uart->rx_index > 0) + { + uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); + } + uart_data_storage_reset(uart); + } + } +} + +/** + * @brief UART接收完成回调函数 + * + * 当UART接收完成时调用此函数。 + * + * @param uart UART设备指针 + */ +void uart_rx_cd_callback(uart_t *uart) +{ + if (uart == NULL) + { + return; + } + if (uart->rx_cd_en == FALSE) + { + return; + } + if (uart->rx_dma_en == FALSE) // 中断方式 + { + if (uart->rx_interupt_cb != NULL && uart->rx_index > 0) + { + uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); + } + uart_data_storage_reset(uart); + } +} + +/** + * @brief 获取UART通信错误计数 + * + * 获取UART设备的接收错误计数。 + * + * @param uart UART设备指针 + * + * @return uint16_t 返回接收错误计数,如果uart为NULL,则返回0 + */ +uint16_t uart_get_error_count(uart_t *uart) +{ + if (uart == NULL) + { + return 0; + } + return uart->rx_error_count; +} + +/** + * @brief 获取UART通信中的错误信息 + * + * 从UART设备中获取接收错误信息和错误计数。 + * + * @param uart UART设备指针 + * @param count 用于存储错误计数的指针 + * + * @return 如果存在错误,则返回错误类型指针;否则返回NULL + */ +uarts_interupt_error_t *uart_get_error(uart_t *uart) +{ + if (uart == NULL) + { + return NULL; + } + if (uart->rx_error_count > 0) + { + + return uart->rx_error; + } + else + { + return NULL; + } +} + +/** + * @brief 重置UART数据存储 + * + * 将UART接收到的数据缓冲区重置,并清除错误状态。 + * + * @param uart UART结构体指针 + */ +void uart_data_storage_reset(uart_t *uart) +{ + if (uart == NULL) + { + return; + } + + uart->rx_index = 0; + uart_clear_error(uart); +} + +/** + * @brief 接收中断回调函数 + * @param {uart_t} *uart UART设备句柄 + * @return {*} 操作结果 + * @note: 该函数用于处理接收中断。首先检查接收DMA使能标志,然后禁用接收中断,配置RX DMA并启用RX DMA通道。当接收到数据时,将数据复制到接收缓冲区,并调用接收中断回调函数。当接收缓冲区满时,关闭RX DMA通道并重置接收索引。 + */ +void uart_reception_callback(uart_t *uart) +{ + // DBG_ASSERT(uart != NULL __DBG_LINE); + if (uart == NULL) + { + return; + } + if (LL_USART_IsEnabledIT_RXNE(uart->huart) && LL_USART_IsActiveFlag_RXNE(uart->huart)) + { + if (uart->rx_index >= uart->rxsize) + { + uart_data_storage_reset(uart); + } + + uart->rxbuf[uart->rx_index++] = LL_USART_ReceiveData8(uart->huart); + uart->rx_interupt_cnt = 0; + uart->rx_interupt_timeout = FALSE; + // 数据交给超时中断处理 :uart_rx_timeout_timer + } + else if (LL_USART_IsEnabledIT_IDLE(uart->huart) && LL_USART_IsActiveFlag_IDLE(uart->huart)) + { + if (uart->rx_dma_en == TRUE) + { + uart->rx_index = uart->rxsize - LL_DMA_GetDataLength(uart->dma, uart->dma_rx_channel); + if (uart->rx_cd_en == FALSE) + { + LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel); + if (uart->rx_interupt_cb != NULL && (uart->rx_index > 0 && uart->rx_index <= uart->rxsize)) + { + uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); + osel_memset(uart->rxbuf, 0, uart->rxsize); + } + + LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize); // 这个不能少 先关闭DMA才能重新设置长度 + LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel); + } + } + + uart->rx_index = 0; + LL_USART_ClearFlag_IDLE(uart->huart); + } + + if (LL_USART_IsEnabledIT_TC(uart->huart) && LL_USART_IsActiveFlag_TC(uart->huart)) + { + if (uart->tx_complete_cb != NULL) + { + uart->tx_complete_cb(); + } + LL_USART_ClearFlag_TC(uart->huart); + } + + if (uart->rx_err_en == TRUE) + { + uarts_interupt_error_e err = UART_NO_ERROR; + if (LL_USART_IsEnabledIT_PE(uart->huart) && LL_USART_IsActiveFlag_PE(uart->huart)) + { + err = UART_PARITY_ERROR; + LL_USART_ClearFlag_PE(uart->huart); // 清除奇偶校验错误标志 + } + + if (LL_USART_IsActiveFlag_FE(uart->huart) && LL_USART_IsActiveFlag_FE(uart->huart)) + { + err = UART_FRAME_ERROR; + LL_USART_ClearFlag_FE(uart->huart); // 清除帧错误标志 + } + + if (LL_USART_IsActiveFlag_NE(uart->huart) && LL_USART_IsActiveFlag_NE(uart->huart)) + { + // err = UART_NOISE_ERROR; + LL_USART_ClearFlag_NE(uart->huart); // 清除噪声错误标志 + } + + if (LL_USART_IsActiveFlag_ORE(uart->huart) && LL_USART_IsActiveFlag_ORE(uart->huart)) + { + err = UART_OVERRUN_ERROR; + LL_USART_ClearFlag_ORE(uart->huart); // 清除溢出错误标志 + } + + if (err != UART_NO_ERROR && uart->rx_error != NULL) + { + uint16_t index = uart->rx_index - 1; + uart->rx_error[uart->rx_error_count].index = index; + uart->rx_error[uart->rx_error_count].err = err; + uart->rx_error_count++; + } + } +} + +/** + * @brief 用于处理串口DMA接收中断的回调函数 + * @param {uart_t} *uart - 串口对象 + * @return {*} 无 + * @note: + */ +void uart_dma_reception_callback(uart_t *uart) +{ + // 检查输入参数是否为空 + DBG_ASSERT(uart != NULL __DBG_LINE); + + uart->tx_dma_ok = TRUE; + + // 禁用串口DMA的发送通道 + LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); + + // 清除发送中断标志位 + DMA_CLEAR_FLAG_TC_CHANNEL(uart->dma, uart->dma_tx_channel); + + // 使能发送中断,用于关闭发送使能引脚 + LL_USART_EnableIT_TC(uart->huart); // 使能发送中断,用于关闭发送使能引脚 + + // 清除传输错误标志 + DMA_CLEAR_FLAG_TE_CHANNEL(uart->dma, uart->dma_tx_channel); +} diff --git a/bsp/uarts.h b/bsp/uarts.h new file mode 100644 index 0000000..fb17c39 --- /dev/null +++ b/bsp/uarts.h @@ -0,0 +1,218 @@ +/** + * @file uarts.h + * @brief Header file for UARTs module. + * + * This file contains the definitions and function prototypes for UARTs module. + * The UARTs module provides functions for creating and managing UART instances, + * enabling reception, sending data, and handling interrupts. + */ + +#ifndef __UARTS_H__ +#define __UARTS_H__ + +#include "lib.h" +#include "main.h" +// 串口中断用于接收超时的参数 +#define RX_TIMEOUT_TICK (1U) /* 10ms的tick */ +#define RX_TIMEOUT_MSEC (20U / RX_TIMEOUT_TICK) /* 20毫秒需要的tick,可以根据需求添加其他时间更短的宏 */ + +/** + * @brief Enumeration for UART status. + */ +typedef enum +{ + UART_OK = 0x00u, /**< The action was successful. */ + UART_ERROR = 0xFFu /**< Generic error. */ +} uart_status_e; + +typedef enum +{ + // 无错误 + UART_NO_ERROR = BIT0, + // 奇偶校验错误中断 + UART_PARITY_ERROR = BIT1, + // 帧错误中断 + UART_FRAME_ERROR = BIT2, + // 噪声错误中断 + UART_NOISE_ERROR = BIT3, + // 溢出错误中断 + UART_OVERRUN_ERROR = BIT4, +} uarts_interupt_error_e; ///< UART中断错误枚举 + +typedef struct +{ + uarts_interupt_error_e err; ///< 错误标志 + uint16_t index; ///< 接收到的第几个字节 +} uarts_interupt_error_t; + +/** + * @brief Callback function type for UART receive interrupt. + * + * This function type is used to define the callback function for UART receive interrupt. + * The callback function is called when data is received on the UART. + * + * @param uart_index The index of the UART. + * @param uart_error The error code. + * @param data The received data. + * @param len The length of the received data. + */ +typedef void (*rx_interupt_cb_t)(uint8_t uart_index, uint8_t *data, uint16_t len); + +/** + * @brief Callback function type for UART transmit complete. + * + * This function type is used to define the callback function for UART transmit complete. + * The callback function is called when the UART transmission is complete. + */ +typedef void (*tx_complete_cb_t)(void); + +/** + * @brief Structure representing a UART instance. + */ +typedef struct +{ + uint8_t uart_index; /**< The index of the UART. */ + USART_TypeDef *huart; /**< The UART peripheral. */ + DMA_TypeDef *dma; /**< The DMA peripheral. */ + uint32_t dma_rx_channel; /**< The DMA receive channel. */ + uint32_t dma_tx_channel; /**< The DMA transmit channel. */ + + //*******************RX*************************/ + BOOL rx_cd_en; /**< Flag indicating if carrier detect is enabled. */ + BOOL rx_dma_en; /**< Flag indicating if DMA reception is enabled. */ + BOOL rx_err_en; /**< Flag indicating if error interrupt is enabled. */ + __IO BOOL rx_interupt_timeout; /**< Flag indicating if receive interrupt timeout. */ + __IO uint8_t rx_interupt_cnt; /**< The receive interrupt count. */ + uint8_t *rxbuf; /**< The receive buffer. */ + uint16_t rxsize; /**< The size of the receive buffer. */ + uarts_interupt_error_t *rx_error; /**< The receive error. */ + uint16_t rx_error_count; /**< The receive error count. */ + __IO uint16_t rx_index; /**< The receive data index. */ + + //*******************TX*************************/ + + BOOL tx_dma_en; /**< Flag indicating if DMA transmission is enabled. */ + uint8_t *txbuf; /**< The transmit buffer. */ + uint16_t txsize; /**< The size of the transmit buffer. */ + uint16_t tx_index; /**< The transmit data index. */ + __IO BOOL tx_dma_ok; /**< Flag indicating if DMA transmission is complete. */ + + rx_interupt_cb_t rx_interupt_cb; /**< The receive interrupt callback function. */ + tx_complete_cb_t tx_complete_cb; /**< The transmit complete callback function. */ +} uart_t; + +/** + * @brief Creates a UART instance. + * + * This function creates a UART instance with the specified parameters. + * + * @param huart The UART peripheral. + * @param rx_dma_en Flag indicating if DMA reception is enabled. + * @param rxsize The size of the receive buffer. + * @param rx_cb The receive interrupt callback function. + * @param tx_dma_en Flag indicating if DMA transmission is enabled. + * @param txsize The size of the transmit buffer. + * @param tx_complete_cb The transmit complete callback function. + * @return The created UART instance. + */ +extern uart_t *uart_create(USART_TypeDef *huart, BOOL rx_dma_en, uint16_t rxsize, rx_interupt_cb_t rx_cb, + BOOL tx_dma_en, uint16_t txsize, tx_complete_cb_t tx_complete_cb); + +/** + * @brief Frees the resources of a UART instance. + * + * This function frees the resources allocated for a UART instance. + * + * @param uart The UART instance to free. + */ +extern void uart_free(uart_t *uart); + +/** + * @brief Initializes a UART instance. + * + * This function initializes the specified UART instance with the specified parameters. + * + * @param uart The UART instance. + * @param baudrate The baudrate. + */ +extern void uart_set_baudrate(USART_TypeDef *uart, uint32_t baudrate); + +/** + * @brief Enables UART reception. + * + * This function enables reception on the specified UART instance. + * + * @param uart The UART instance. + */ +extern void uart_recv_en(uart_t *uart, BOOL rx_err_en); + +/** + * @brief Sends data over UART. + * + * This function sends the specified data over the specified UART instance. + * + * @param uart The UART instance. + * @param data The data to send. + * @param len The length of the data. + */ +extern void uart_send_data(uart_t *uart, uint8_t *data, uint16_t len); + +/** + * @brief UART receive carrier detect callback. + * + * This function is the callback for UART receive carrier detect. + * + * @param uart The UART instance. + */ +extern void uart_rx_cd_callback(uart_t *uart); + +/** + * @brief UART receive timeout timer. + * + * This function is the timer callback for UART receive timeout. + * + * @param uart The UART instance. + */ +extern void uart_rx_timeout_timer(uart_t *uart); +/** + * @brief UART receive interrupt callback. + * + * This function is the interrupt callback for UART receive interrupt. + * + * @param uart The UART instance. + */ +extern void uart_reception_callback(uart_t *uart); + +/** + * @brief Get the UART error count. + * + * This function returns the number of errors that have occurred on the specified UART instance. + * + * @param uart The UART instance. + * @return The number of errors. + */ +extern uint16_t uart_get_error_count(uart_t *uart); + +/** + * @brief Get UART interrupt error. + * + * This function gets the UART interrupt error. + * + * @param uart The UART instance. + * @param count The error count. + * @return The error. + */ +extern uarts_interupt_error_t *uart_get_error(uart_t *uart); + +extern void uart_data_storage_reset(uart_t *uart); + +/** + * @brief DMA receive interrupt callback. + * + * This function is the interrupt callback for DMA receive interrupt. + * + * @param uart The UART instance. + */ +extern void uart_dma_reception_callback(uart_t *uart); + +#endif ///< __UARTS_H__ diff --git a/inc/btn.h b/inc/btn.h new file mode 100644 index 0000000..a92e576 --- /dev/null +++ b/inc/btn.h @@ -0,0 +1,164 @@ +/*** + * @Author: + * @Date: 2023-07-04 08:26:12 + * @LastEditors: xxx + * @LastEditTime: 2023-08-09 22:49:14 + * @Description: btn是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式 + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +/** +使用方法: +1.先申请一个按键结构 + +struct Button button1; +2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平 + +button_init(&button1, read_button_pin, 0, 0); +3.注册按键事件 + +button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler); +button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler); +... +4.启动按键 + +button_start(&button1); +5.设置一个5ms间隔的定时器循环调用后台处理函数 + +while(1) { + ... + if(timer_ticks == 5) { + timer_ticks = 0; + + button_ticks(); + } +} +*/ +#ifndef _BTN_H_ +#define _BTN_H_ + +#ifdef STM32 +#include "lib.h" +#else +#include "stdint.h" +#include "string.h" +#endif + +// 根据您的需求修改常量。 +#define TICKS_INTERVAL 10 // 按钮扫描间隔,单位ms +#define DEBOUNCE_TICKS 30 / TICKS_INTERVAL // 按键去抖动时间,单位ms +#define SHORT_TICKS (30 / TICKS_INTERVAL) // 短按时间阈值,单位ms +#define LONG_TICKS (500 / TICKS_INTERVAL) // 长按时间阈值,单位ms + +typedef void (*BtnCallback)(void *); + +typedef enum +{ + ACTIVE_LEVEL_LOW = 0, // 低电平有效 + ACTIVE_LEVEL_HIGH, // 高电平有效 +} active_level_e; + +/*** + * @brief + 事件 说明 + PRESS_DOWN 按键按下,每次按下都触发 + PRESS_UP 按键弹起,每次松开都触发 + PRESS_REPEAT 重复按下触发,变量repeat计数连击次数 + SINGLE_CLICK 单击按键事件 + DOUBLE_CLICK 双击按键事件 + LONG_PRESS_START 达到长按时间阈值时触发一次 + LONG_PRESS_HOLD 长按期间一直触发 + */ +typedef enum +{ + PRESS_DOWN = 0, // 按下 + PRESS_UP, // 弹起 + PRESS_REPEAT, // 重复按下 + SINGLE_CLICK, // 单击 + DOUBLE_CLICK, // 双击 + LONG_PRESS_START, // 长按开始 + LONG_PRESS_HOLD, // 长按保持 + number_of_event, // 事件数量 + NONE_PRESS // 无按键 +} PressEvent; + +/*** + * @brief 按钮设备结构体 + */ +typedef struct Button +{ + uint16_t ticks; // 计时器 + uint8_t repeat : 4; // 重复计数 + uint8_t event : 4; // 事件类型 + uint8_t state : 3; // 状态 + uint8_t debounce_cnt : 3; // 去抖计数 + uint8_t active_level : 1; // 激活电平 + uint8_t button_level : 1; // 按钮电平 + uint8_t button_id; // 按钮ID + uint8_t button_id_reverse; // 按钮ID反转 + uint8_t (*hal_button_Level)(uint8_t button_id_); // 获取按钮引脚电平函数 + BtnCallback cb[number_of_event]; // 回调函数数组 + struct Button *next; // 下一个按钮句柄 +} Button; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief 初始化按钮设备 + * @param handle: 按钮句柄结构体 + * @param pin_level: 获取按钮引脚电平函数 + * @param active_level: 按钮激活电平 + * @param button_id: 按钮ID + * @retval 0: 成功 + * @retval -1: 失败 + */ + void button_init(struct Button *handle, uint8_t (*pin_level)(uint8_t), active_level_e active_level, uint8_t button_id, uint8_t button_id_reverse); + + /** + * @brief 附加按钮事件处理函数 + * @param handle: 按钮句柄结构体 + * @param event: 按钮事件 + * @param cb: 回调函数 + * @retval 0: 成功 + * @retval -1: 失败 + */ + void button_attach(struct Button *handle, PressEvent event, BtnCallback cb); + + /** + * @brief 获取按钮事件 + * @param handle: 按钮句柄结构体 + * @retval 按钮事件 + */ + PressEvent get_button_event(struct Button *handle); + + /** + * @brief 启动按钮工作 + * @param handle: 按钮句柄结构体 + * @retval 0: 成功 + * @retval -1: 已存在 + */ + int button_start(struct Button *handle); + + /** + * @brief 停止按钮工作 + * @param handle: 按钮句柄结构体 + * @retval None + */ + void button_stop(struct Button *handle); + + /** + * @brief 后台计时,定时器重复调用间隔为5ms + * @param None + * @retval None + */ + void button_ticks(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/delay.h b/inc/delay.h new file mode 100644 index 0000000..ac129de --- /dev/null +++ b/inc/delay.h @@ -0,0 +1,21 @@ +/*** + * @Author: + * @Date: 2023-04-11 18:31:07 + * @LastEditors: xxx + * @LastEditTime: 2023-04-11 18:31:20 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __DELAY_H +#define __DELAY_H + +#include "sys.h" + +void delay_init(uint16_t sysclk); /* 初始化延迟函数 */ +void delay_ms(uint16_t nms); /* 延时nms */ +void delay_us(uint32_t nus); /* 延时nus */ +void delay_tick(uint32_t ticks); /* 延时ticks */ + +#endif diff --git a/inc/sys.h b/inc/sys.h new file mode 100644 index 0000000..2517e5b --- /dev/null +++ b/inc/sys.h @@ -0,0 +1,67 @@ +/*** + * @Author: + * @Date: 2023-04-11 18:46:58 + * @LastEditors: xxx + * @LastEditTime: 2023-04-11 22:16:47 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef _SYS_H +#define _SYS_H + +#include "main.h" +#include "lib.h" + +#define CLOCK_CHANGE_ENABLE FALSE ///< 时钟切换使能 +#define LOCK() __disable_irq() ///< 系统关全局中断 +#define UNLOCK() __enable_irq() ///< 系统开全局中断 + +typedef struct +{ + uint32_t pll_source; + uint32_t pll_m; + uint32_t pll_n; + uint32_t pll_r; + + uint32_t ahb_div; + uint32_t apb1_div; + uint32_t apb2_div; + uint32_t sysclk; + + // private: + uint32_t sysclk_change; // 改变后的系统时钟 这个参数由change_system_clock函数设置,作为内部观察使用 +} clock_config_t; /* 时钟配置结构体 */ + +typedef struct +{ + uint32_t ticks_max; // 最大ticks + uint32_t ticks_current; // 当前使用的ticks + uint32_t uticks; // 使用的ticks,用于计算 +} utime_ticks_t; + +void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset); /* 设置中断偏移量 */ +void sys_standby(void); /* 进入待机模式 */ +void sys_soft_reset(void); /* 系统软复位 */ +void LL_IncTick(void); /* 系统滴答时钟 */ +void sys_millis_reset(void); /* 系统计时器重新开始 */ +uint32_t sys_get_tick(void); /* 获取系统滴答时钟 */ +uint32_t sys_millis(void); /* 获取系统时间 */ +uint32_t sys_to_seconds(uint32_t start_time); /* 将系统时间转换为秒 */ + +// 以下为汇编函数 +void sys_wfi_set(void); /* 执行WFI指令 */ +void sys_intx_disable(void); /* 关闭所有中断 */ +void sys_intx_enable(void); /* 开启所有中断 */ +void sys_msr_msp(uint32_t addr); /* 设置栈顶地址 */ + +void scheduler_time_start(void); +uint32_t scheduler_time_stop(void); +uint32_t scheduler_time_occupancy_get(uint32_t run_time); + +void system_clock_read(void); +void restore_system_clock(void); +clock_config_t *get_original_clock_config(void); +void change_system_clock(clock_config_t *new_config); +#endif diff --git a/lib/bootload/bootload.c b/lib/bootload/bootload.c new file mode 100644 index 0000000..6dacd43 --- /dev/null +++ b/lib/bootload/bootload.c @@ -0,0 +1,229 @@ +#include "bootload.h" +#include "ymodem.h" +#include "sys.h" +#include "delay.h" +#include "cmac.h" +#define AES_CMAC_DIGEST_LENGTH 16 +typedef void (*fnc_ptr)(void); // 用于跳转到应用程序的函数指针 +static bootload_transmit_callback transmit_callback = NULL; +static bootload_end_callback end_callback = NULL; +uint8_t upgrade_key[] = + { + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥 + +static AES_CMAC_CTX upgrade_ctx; /**< AES CMAC context for upgrade process */ +static uint8_t pre_upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) before upgrade */ +static uint8_t upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) after upgrade */ +static uint32_t flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START; /**< Flash destination address for bootloading */ +static uint32_t upgrade_size = 0; /**< Size of the upgrade */ +static uint8_t read_cache[LL_FLASH_PAGE_SIZE]; /**< Read cache for flash pages */ +static uint8_t data_src_from; /**< Data source for bootloading */ + +/** + * @brief Perform bootload inspection. + * + * This function calls the `rym_process()` function to perform bootload inspection. + */ +void bootload_inspection(void) +{ + rym_process(); +} + +/** + * @brief Checks if the bootload timeout has occurred. + * + * This function calls the `rym_timeout()` function to determine if the bootload timeout has occurred. + * + * @return TRUE if the bootload timeout has occurred, FALSE otherwise. + */ +BOOL bootload_timeout(void) +{ + return rym_timeout(); +} + +/** + * @brief Handles the beginning of the bootloading process. + * + * This function is responsible for handling the initial steps of the bootloading process. + * It checks if the length of the data to be bootloaded is greater than the end address of the application area. + * If the length is greater, it returns an error code indicating that the bootloading cannot proceed. + * Otherwise, it sets the flash destination address to the start address of the backup area, + * erases the bank of the flash memory where the application is stored, + * and initializes the AES-CMAC context for the upgrade process. + * + * @param p Pointer to the data to be bootloaded. + * @param len Length of the data to be bootloaded. + * @return Error code indicating the result of the operation. + * - RYM_CODE_CAN: If the length of the data is greater than the end address of the application area. + * - RYM_CODE_NONE: If the operation is successful. + */ +static rym_code_e on_begin(uint8_t *p, uint32_t len) +{ + if (len > BOOTLOAD_APP_END_ADDRESS) + { + return RYM_CODE_CAN; + } + + flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START; + LL_FLASH_Unlock(FLASH); + LL_FLASH_EraseBank(LL_FLASH_BANK2); + LL_FLASH_Lock(FLASH); + + AES_CMAC_Init(&upgrade_ctx); + AES_CMAC_SetKey(&upgrade_ctx, upgrade_key); + return RYM_CODE_NONE; +} + +/** + * @brief Handles the received data and programs it into the flash memory. + * + * This function unlocks the flash memory, programs the received data into the specified flash destination, + * locks the flash memory again, updates the AES-CMAC context with the received data, and increments the flash destination. + * + * @param p Pointer to the data buffer. + * @param len Length of the data buffer. + * @return The result code indicating the success or failure of the operation. + */ +static rym_code_e on_data(uint8_t *p, uint32_t len) +{ + LL_FLASH_Unlock(FLASH); + LL_FLASH_Program(flashdestination, p, len); + LL_FLASH_Lock(FLASH); + AES_CMAC_Update(&upgrade_ctx, p, len); + flashdestination += len; + return RYM_CODE_NONE; +} + +/** + * @brief Calculate the MIC (Message Integrity Code) for the firmware upgrade. + * + * This function calculates the MIC using AES-CMAC algorithm for the firmware upgrade data. + * It reads the firmware data from flash memory in pages and updates the MIC calculation. + * Finally, it compares the calculated MIC with the pre-upgrade MIC to determine the success of the upgrade. + * + * @param p Pointer to the firmware data. + * @param len Length of the firmware data. + * @return The result code indicating the success or failure of the upgrade process. + */ +static rym_code_e on_end(uint8_t *p, uint32_t len) +{ + AES_CMAC_Final(pre_upgrade_mic, &upgrade_ctx); + upgrade_size = flashdestination - BOOTLOAD_APP_BACKUP_ADDR_START; + + AES_CMAC_Init(&upgrade_ctx); + AES_CMAC_SetKey(&upgrade_ctx, upgrade_key); + uint32_t start = BOOTLOAD_APP_BACKUP_ADDR_START; + uint16_t num = (upgrade_size / LL_FLASH_PAGE_SIZE); + uint16_t remain = (upgrade_size % LL_FLASH_PAGE_SIZE); + + // STM32L476RG的flash页大小为2K,先读取整数页,再读取余数 + for (uint16_t i = 0; i < num; i++) + { + LL_FLASH_Read((start + i * (LL_FLASH_PAGE_SIZE)), read_cache, LL_FLASH_PAGE_SIZE); + AES_CMAC_Update(&upgrade_ctx, read_cache, LL_FLASH_PAGE_SIZE); + } + + if (remain) + { + osel_memset(read_cache, 0, LL_FLASH_PAGE_SIZE); + LL_FLASH_Read((start + num * (LL_FLASH_PAGE_SIZE)), read_cache, remain); + AES_CMAC_Update(&upgrade_ctx, read_cache, remain); + } + + AES_CMAC_Final(upgrade_mic, &upgrade_ctx); + + // 比较mic,相同可以写入标志位告知应用程序升级成功 + if (osel_memcmp(upgrade_mic, pre_upgrade_mic, AES_CMAC_DIGEST_LENGTH) == 0) + { + end_callback(TRUE); + } + else + { + end_callback(FALSE); + } + return RYM_CODE_NONE; +} + +/** + * @brief Handles the transmission of data. + * + * This function calls the transmit_callback function to transmit the data from the specified source. + * + * @param p Pointer to the data buffer. + * @param len Length of the data buffer. + * @return The return code indicating the status of the transmission. + */ +static rym_code_e on_transmit(uint8_t *p, uint32_t len) +{ + transmit_callback(data_src_from, p, (uint16_t)len); + return RYM_CODE_NONE; +} + +/** + * @brief Initializes the bootload module. + * + * This function initializes the bootload module by setting the transmit callback + * and configuring the RYM module. It asserts if the initialization fails. + * + * @param transmit The transmit callback function. + */ +void bootload_init(bootload_transmit_callback transmit, bootload_end_callback end) +{ + BOOL res = FALSE; + + transmit_callback = transmit; + end_callback = end; + + res = rym_init(); + DBG_ASSERT(res == TRUE __DBG_LINE); + + res = rym_config(on_begin, on_data, on_end, on_transmit, BOOTLOAD_TIMEOUT); + DBG_ASSERT(res == TRUE __DBG_LINE); +} + +/** + * @brief Sets the data source index for bootload transmission. + * + * This function sets the data source index for bootload transmission. The data source index + * determines the starting point from which the data will be transmitted. + * + * @param to_index The index of the data source. + */ +void bootload_transmit_from(const uint8_t to_index) +{ + data_src_from = to_index; +} + +/** + * @brief Jump to the specified address and execute the bootloader. + * + * @param address The entry address of the bootloader. + */ +void bootload_jump(uint32_t address) +{ + // Get the entry address of the application program + fnc_ptr jump_to_bootload; + jump_to_bootload = (fnc_ptr)(*(__IO uint32_t *)(address + 4)); + + // Disable RCC + RCC->APB1ENR1 = 0; + RCC->APB1ENR2 = 0; + RCC->APB2ENR = 0; + RCC->AHB1ENR = 0; + RCC->AHB2ENR = 0; + RCC->AHB3ENR = 0; + + // Disable SysTick + SysTick->CTRL = 0; + // 清空SysTick + SysTick->LOAD = 0; + // 清空SysTick + SysTick->VAL = 0; + // 设置向量表偏移地址 + SCB->VTOR = address; + // 设置堆栈指针 + sys_msr_msp(*(__IO uint32_t *)address); + // 跳转 + jump_to_bootload(); +} diff --git a/lib/bootload/bootload.h b/lib/bootload/bootload.h new file mode 100644 index 0000000..e42c506 --- /dev/null +++ b/lib/bootload/bootload.h @@ -0,0 +1,102 @@ +#ifndef __BOOTLOAD_H +#define __BOOTLOAD_H +#include "lib.h" +#include "flash.h" +#include "flow.h" + +#define BOOTLOAD_SET_FLAG 0xbb01 +#define BOOTLOAD_UNSET_FLAG 0xbb02 +/** + * @brief Defines the start address of the application in flash memory. + */ +#define BOOTLOAD_APP_START_ADDRESS ((uint32_t)0x08000000u) + +/** + * @brief Defines the end address of the application in flash memory. + */ +#define BOOTLOAD_APP_END_ADDRESS (BOOTLOAD_APP_START_ADDRESS + 220 * LL_FLASH_PAGE_SIZE) // 220*2048 = 450560 + +/** + * @brief Defines the start address of the bootloader in flash memory. + */ +#define BOOTLOAD_START_ADDRESS BOOTLOAD_APP_END_ADDRESS + +/** + * @brief Defines the start address of the backup area for the application in flash memory. + */ +#define BOOTLOAD_APP_BACKUP_ADDR_START (BOOTLOAD_APP_START_ADDRESS + 256 * LL_FLASH_PAGE_SIZE) + +/** + * @brief Defines the timeout for the bootloading process. + * + * This macro defines the timeout for the bootloading process, in seconds. The default value is 10 seconds. + */ +#define BOOTLOAD_TIMEOUT 10 + +/** + * @brief A function pointer type for a bootload transmit callback function. + * + * This function pointer type is used for a bootload transmit callback function, which is + * called when data needs to be transmitted to the bootloader. The function is passed the + * source of the data (the index of the data packet), a pointer to the data buffer, and the + * length of the data buffer. + * + * @param data_src The index of the data packet that is being transmitted. + * @param buf A pointer to the data buffer. + * @param len The length of the data buffer. + */ +typedef void (*bootload_transmit_callback)(const uint8_t data_src, const uint8_t *buf, const uint16_t len); + +/** + * @brief A function pointer type for a bootload end callback function. + * + * This function pointer type is used for a bootload end callback function, which is called + * when the bootloading process is complete. The function takes no parameters and returns no + * value. + */ +typedef void (*bootload_end_callback)(BOOL); + +/** + * @brief initializes the bootloader + * + * This function initializes the bootloader, including setting up the communication + * with the host and configuring the flash memory for bootloading. + * + * @param transmit a pointer to the function that will be called to transmit data to the host + */ +void bootload_init(bootload_transmit_callback transmit, bootload_end_callback end); + +/** + * @brief Transmits data from the specified index. + * + * This function transmits data from the specified index to the bootloader. + * + * @param to_index The index from which the data should be transmitted. + */ +void bootload_transmit_from(const uint8_t to_index); + +/** + * @brief Jumps to the specified address. + * + * This function jumps to the specified address, which is typically the start address of the bootloader. + * + * @param address The address to jump to. + */ +void bootload_jump(uint32_t address); + +/** + * @brief Performs inspection of the bootloader. + * + * This function performs inspection of the bootloader, such as checking the version or integrity of the bootloader. + */ +void bootload_inspection(void); + +/** + * @brief Checks if a timeout has occurred. + * + * This function checks if a timeout has occurred during the bootloading process. + * + * @return TRUE if a timeout has occurred, FALSE otherwise. + */ +BOOL bootload_timeout(void); +#endif // __BOOTLOAD_H diff --git a/lib/bootload/readme.md b/lib/bootload/readme.md new file mode 100644 index 0000000..d0231d7 --- /dev/null +++ b/lib/bootload/readme.md @@ -0,0 +1,50 @@ + +# BOOTLOADER介绍 + + +STM32的BOOTLOADER是在芯片复位或从停机模式唤醒时执行的一段小程序,它负责将用户代码加载到内存中并启动它。STM32F1、F4、H7等不同系列的MCU可能会有不同的BOOTLOADER程序。 + +BOOTLOADER通常用于以下几种情况: + +1. 在应用程序无法正常启动时,提供一个后备启动方式。 +2. 在系统需要进行固件更新时,可以先通过BOOTLOADER加载新的用户代码。 +3. 用于在线调试或调试无法通过JTAG/SWD接口访问时,可以通过BOOTLOADER加载调试工具。 + +BOOTLOADER的设计和实现通常依赖于芯片的内部结构和特性,以及用户代码存储的介质(如内部FLASH,外部SPI FLASH等)。 + +一个简单的BOOTLOADER示例可能包括以下步骤: + +1. 复位后,芯片开始执行内部的BOOTLOADER程序。 +2. 通过某种通信接口(如USART,I2C,SPI)接收新的用户程序代码。 +3. 将接收到的代码写入用户代码存储区(如内部FLASH)。 +4. 设置启动引脚或者配置BOOT引导模式寄存器,选择启动用户代码。 +5. 重启芯片,这次不再执行BOOTLOADER,而是加载并运行新的用户程序代码。 + +注意:实际的BOOTLOADER实现可能会更加复杂,包括错误检查和处理、加密解密、固件完整性校验等安全措施。 + +# bootload.c 文件说明 + +`bootload.c`是一个实现引导加载程序(bootloader)功能的源代码文件。引导加载程序是一段在系统启动时运行的代码,负责初始化硬件设备、建立内存空间映射图,然后加载操作系统内核并将控制权转交给它。 + +以下是 `bootload.c`文件中可能包含的主要函数和其功能: + +1. **系统启动函数** :这个函数是引导加载程序的入口点,它负责启动整个引导加载过程。 +2. **硬件初始化函数** :这些函数负责初始化系统的硬件设备,包括CPU、内存、IO设备等。 +3. **内存映射设置函数** :这些函数负责建立内存空间的映射图,包括物理内存、虚拟内存的映射关系。 +4. **操作系统内核加载函数** :这些函数负责加载操作系统内核,包括从存储设备读取内核镜像,加载到内存中,然后跳转到内核的入口点。 +5. **错误处理函数** :这些函数负责处理在引导加载过程中可能出现的各种错误,包括硬件错误、内核加载错误等。 + + + +# ymodem.c 文件说明 + +`ymodem.c`是一个实现YMODEM协议的源代码文件。YMODEM是一种用于文件传输的协议,它在XMODEM协议的基础上增加了一些新的特性,例如支持更大的文件和文件名传输。 + +以下是 `ymodem.c`文件中可能包含的主要函数和其功能: + +1. **初始化和结束传输的函数** :这些函数负责设置传输的开始和结束,包括打开和关闭必要的硬件接口,设置传输参数等。 +2. **发送和接收数据包的函数** :这些函数负责实际的数据传输,包括将数据打包成YMODEM格式的数据包,通过硬件接口发送和接收数据包,处理数据包的确认和重传等。 +3. **CRC校验的函数** :这些函数负责计算和检查数据包的CRC(循环冗余校验)值,以确保数据的完整性。 +4. **处理错误和重试的函数** :这些函数负责处理在传输过程中可能出现的各种错误,包括数据包丢失、CRC校验失败等,并在必要时进行重试。 + +此外,`ymodem.c`文件还可能包含一些辅助函数,用于处理如超时、缓冲区管理等问题。 diff --git a/lib/bootload/test_ymodem.c b/lib/bootload/test_ymodem.c new file mode 100644 index 0000000..d7c6b08 --- /dev/null +++ b/lib/bootload/test_ymodem.c @@ -0,0 +1,79 @@ +#include "unity.h" +#include "ymodem.c" + +void setUp(void) +{ + // 这里可以进行每个测试用例开始前的设置 +} + +void tearDown(void) +{ + // 这里可以进行每个测试用例结束后的清理 +} + +void test_CRC16(void) +{ + unsigned char data[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + TEST_ASSERT_EQUAL_HEX16(EXPECTED_CRC_VALUE, CRC16(data, sizeof(data))); +} + +void test_IS_CAP_LETTER(void) +{ + TEST_ASSERT_TRUE(IS_CAP_LETTER('A')); + TEST_ASSERT_FALSE(IS_CAP_LETTER('a')); +} + +void test_IS_LC_LETTER(void) +{ + TEST_ASSERT_TRUE(IS_LC_LETTER('a')); + TEST_ASSERT_FALSE(IS_LC_LETTER('A')); +} + +void test_IS_09(void) +{ + TEST_ASSERT_TRUE(IS_09('0')); + TEST_ASSERT_FALSE(IS_09('A')); +} + +void test_ISVALIDHEX(void) +{ + TEST_ASSERT_TRUE(ISVALIDHEX('A')); + TEST_ASSERT_TRUE(ISVALIDHEX('a')); + TEST_ASSERT_TRUE(ISVALIDHEX('0')); + TEST_ASSERT_FALSE(ISVALIDHEX('G')); +} + +void test_ISVALIDDEC(void) +{ + TEST_ASSERT_TRUE(ISVALIDDEC('0')); + TEST_ASSERT_FALSE(ISVALIDDEC('A')); +} + +void test_CONVERTDEC(void) +{ + TEST_ASSERT_EQUAL_HEX8(0, CONVERTDEC('0')); + TEST_ASSERT_EQUAL_HEX8(9, CONVERTDEC('9')); +} + +void test_CONVERTHEX(void) +{ + TEST_ASSERT_EQUAL_HEX8(10, CONVERTHEX('A')); + TEST_ASSERT_EQUAL_HEX8(10, CONVERTHEX('a')); + TEST_ASSERT_EQUAL_HEX8(0, CONVERTHEX('0')); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(test_CRC16); + RUN_TEST(test_IS_CAP_LETTER); + RUN_TEST(test_IS_LC_LETTER); + RUN_TEST(test_IS_09); + RUN_TEST(test_ISVALIDHEX); + RUN_TEST(test_ISVALIDDEC); + RUN_TEST(test_CONVERTDEC); + RUN_TEST(test_CONVERTHEX); + + return UNITY_END(); +} diff --git a/lib/bootload/ymodem.c b/lib/bootload/ymodem.c new file mode 100644 index 0000000..8bafe13 --- /dev/null +++ b/lib/bootload/ymodem.c @@ -0,0 +1,633 @@ +/** + * @file ymodem.c + * @author xxx + * @date 2024-02-18 19:32:40 + * @brief + * 该模块实现了YMODEM协议的核心功能,包括初始化、配置、数据接收、握手、数据传输和结束处理。 + * 它使用了回调函数来处理不同阶段的事件,并使用信号量来同步不同的流程。CRC校验是用来确保数据传输的完整性和准确性 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#include "ymodem.h" +#include "sys.h" +#include "delay.h" +#include "flow.h" + +sqqueue_ctrl_t rym_sqqueue; // 一个接收队列控制结构体,用于管理接收到的数据。 +static uint32_t tm_sec = 0; // 握手超时时间,用于握手阶段的超时计时 +static enum rym_stage stage = RYM_STAGE_NONE; // 当前的阶段 +static int32_t rym_tm_sec = 0; // YMODEM超时计时器 + +static uint8_t aPacketData[_RYM_PKG_SZ]; // 数据包缓冲区 + +// 回调函数,用于处理不同阶段的事件 +static rym_callback rym_on_begin = NULL; +static rym_callback rym_on_data = NULL; +static rym_callback rym_on_end = NULL; +static rym_callback rym_transmit = NULL; + +static struct flow handshake_fw; // 握手流程 +static struct flow trans_fw; // 传输流程 +static struct flow finsh_fw; // 结束流程 +static struct flow_sem msg_sem; // 消息信号量,用于同步 + +static const uint16_t ccitt_table[256] = + { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; + +uint16_t CRC16(unsigned char *q, int len) +{ + uint16_t crc = 0; + + while (len-- > 0) + crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff]; + return crc; +} + +/* Exported macro ------------------------------------------------------------*/ +#define IS_CAP_LETTER(c) (((c) >= 'A') && ((c) <= 'F')) +#define IS_LC_LETTER(c) (((c) >= 'a') && ((c) <= 'f')) +#define IS_09(c) (((c) >= '0') && ((c) <= '9')) +#define ISVALIDHEX(c) (IS_CAP_LETTER(c) || IS_LC_LETTER(c) || IS_09(c)) +#define ISVALIDDEC(c) IS_09(c) +#define CONVERTDEC(c) (c - '0') + +#define CONVERTHEX_ALPHA(c) (IS_CAP_LETTER(c) ? ((c) - 'A' + 10) : ((c) - 'a' + 10)) +#define CONVERTHEX(c) (IS_09(c) ? ((c) - '0') : CONVERTHEX_ALPHA(c)) +/** + * @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 Read data from the circular queue. + * + * This function reads the specified length of data from the circular queue and stores it in the given buffer. + * If the length of data in the queue is greater than or equal to the specified length, it directly reads the specified length of data. + * If the length of data in the queue is less than the specified length, it reads all the data in the queue. + * + * @param buffer The buffer to store the data. + * @param len The length of data to read. + * + * @return The actual length of data read. + */ +static uint16_t rym_sqqueue_read(void *buffer, uint16_t len) +{ + uint16_t i = 0; + uint8_t *buf = buffer; + if (rym_sqqueue.get_len(&rym_sqqueue) >= len) + { + for (i = 0; i < len; i++) + { + buf[i] = *((uint8_t *)rym_sqqueue.del(&rym_sqqueue)); + } + } + else + { + while ((rym_sqqueue.get_len(&rym_sqqueue) != 0) && (i < len)) + { + buf[i] = *((uint8_t *)rym_sqqueue.del(&rym_sqqueue)); + } + } + + return i; +} + +/** + * @brief Initializes the parameters for the YMODEM process. + * + * This function sets the stage variable to the specified stage and initializes the rym_tm_sec variable with the value of tm_sec. + * If the stage is RYM_STAGE_ESTABLISHING, it also clears the rym_sqqueue. + * + * @param st The stage of the YMODEM process. + */ +static void rym_process_params_init(enum rym_stage st) +{ + stage = st; + rym_tm_sec = tm_sec; + if (st == RYM_STAGE_ESTABLISHING) + { + rym_sqqueue.clear_sqq(&rym_sqqueue); + } +} + +/** + * @brief Performs the handshake process for the YMODEM protocol. + * + * This function reads packets from the input queue and performs the necessary checks and actions + * to establish a connection and initiate file transfer using the YMODEM protocol. + * + * @param fl The flow structure containing the necessary variables and synchronization mechanisms. + * @return The result of the handshake process. + * - 0: Handshake process completed successfully. + * - Non-zero: Handshake process failed. + */ +static uint8_t rym_do_handshake_process(struct flow *fl) +{ + uint8_t index = 0; + FL_HEAD(fl); + static uint16_t rym_recv_len = 0; + static uint16_t recv_crc, cal_crc; + static uint8_t *file_ptr = NULL; + static uint8_t file_name[FILE_NAME_LENGTH]; + static uint8_t file_size[FILE_SIZE_LENGTH]; + static uint32_t filesize; + for (;;) + { + FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC); + if (FL_SEM_IS_RELEASE(fl, &msg_sem)) + { + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1); + if (rym_recv_len == 1) + { + if (aPacketData[PACKET_START_INDEX] != RYM_CODE_SOH && aPacketData[PACKET_START_INDEX] != RYM_CODE_STX) + continue; + + /* SOH/STX + seq + payload + crc */ + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX], + _RYM_PKG_SZ - 1); + if (rym_recv_len != (_RYM_PKG_SZ - 1)) + continue; + /* sanity check */ + if ((aPacketData[PACKET_NUMBER_INDEX] != 0) || (aPacketData[PACKET_CNUMBER_INDEX] != 0xFF)) + continue; + recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + _RYM_PKG_SZ - 2) << 8) | + *(&aPacketData[PACKET_START_INDEX] + _RYM_PKG_SZ - 1); + cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, _RYM_PKG_SZ - 5); + if (recv_crc != cal_crc) + continue; + + if (rym_on_begin != NULL) + { + file_ptr = aPacketData + PACKET_DATA_INDEX; + while ((*file_ptr != 0) && (index < FILE_NAME_LENGTH)) + { + file_name[index++] = *file_ptr++; + } + file_name[index++] = '\0'; + index = 0; + file_ptr++; + while ((*file_ptr != ' ') && (index < FILE_SIZE_LENGTH)) + { + file_size[index++] = *file_ptr++; + } + file_size[index++] = '\0'; + Str2Int(file_size, &filesize); + + if (RYM_CODE_NONE != rym_on_begin(file_name, filesize)) + { + for (uint8_t i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++) + { + aPacketData[0] = RYM_CODE_CAN; + rym_transmit(aPacketData, 1); + } + } + else + { + aPacketData[0] = RYM_CODE_ACK; + rym_transmit(aPacketData, 1); + FL_LOCK_DELAY(fl, FL_CLOCK_100MSEC * 5); + aPacketData[0] = RYM_CODE_C; + rym_transmit(aPacketData, 1); + rym_process_params_init(RYM_STAGE_TRANSMITTING); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC); + } + } + } + } + else + { + aPacketData[0] = RYM_CODE_C; + rym_transmit(aPacketData, 1); + rym_tm_sec--; + } + } + FL_TAIL(fl); +} + +/** + * @brief Transfers data using the YMODEM protocol. + * + * This function is responsible for transferring data using the YMODEM protocol. + * It receives the size of the data to be transferred and a pointer to the code + * that will be returned. It performs various checks on the received data and + * calculates the CRC to ensure data integrity. If all checks pass, it sets the + * code to RYM_CODE_ACK and returns 0. Otherwise, it returns an error code. + * + * @param data_size The size of the data to be transferred. + * @param code Pointer to the code that will be returned. + * @return 0 if successful, otherwise an error code. + */ +static int8_t rym_tran_data(uint16_t data_size, uint8_t *code) +{ + DBG_ASSERT(NULL != code __DBG_LINE); + uint16_t recv_len = 0; + uint16_t recv_crc, cal_crc; + const uint16_t tran_size = PACKET_HEADER_SIZE - 1 + data_size + PACKET_TRAILER_SIZE; + + /* seq + data + crc */ + recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX], + tran_size); + if (recv_len != tran_size) + return -RYM_ERR_DSZ; + /* sanity check */ + if ((aPacketData[PACKET_NUMBER_INDEX] + aPacketData[PACKET_CNUMBER_INDEX]) != 0xFF) + return -RYM_ERR_SEQ; + /* As we are sending C continuously, there is a chance that the + * sender(remote) receive an C after sending the first handshake package. + * So the sender will interpret it as NAK and re-send the package. So we + * just ignore it and proceed. */ + if (stage == RYM_STAGE_ESTABLISHED && aPacketData[PACKET_NUMBER_INDEX] == RYM_CODE_NONE) + { + *code = RYM_CODE_NONE; + return 0; + } + + stage = RYM_STAGE_TRANSMITTING; + + recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + tran_size - 1) << 8) | + *(&aPacketData[PACKET_START_INDEX] + tran_size); + cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, data_size); + if (recv_crc != cal_crc) + return -RYM_ERR_CRC; + + *code = RYM_CODE_ACK; + return 0; +} + +/** + * @brief Performs the YMODEM transmission process. + * + * This function is responsible for handling the YMODEM transmission process. + * It receives packets of data and performs the necessary operations based on the received data. + * It handles timeouts and retransmissions if necessary. + * + * @param fl The flow structure pointer. + * @return The status of the transmission process. + */ +static uint8_t rym_do_trans_process(struct flow *fl) +{ + FL_HEAD(fl); + static uint16_t data_size, rym_recv_len; + static uint8_t rym_code; + static uint16_t tran_timeout = 0; + for (;;) + { + FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC); + if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem)) + { + if (tran_timeout++ >= 5) + { + tran_timeout = 0; + rym_process_params_init(RYM_STAGE_ESTABLISHING); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC); + } + } + else + { + tran_timeout = 0; + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1); + if (rym_recv_len == 1) + { + if (aPacketData[PACKET_START_INDEX] == RYM_CODE_SOH) + data_size = PACKET_SIZE; + else if (aPacketData[PACKET_START_INDEX] == RYM_CODE_STX) + data_size = PACKET_1K_SIZE; + else if (aPacketData[PACKET_START_INDEX] == RYM_CODE_EOT) + { + aPacketData[0] = RYM_CODE_NAK; + rym_transmit(aPacketData, 1); + rym_process_params_init(RYM_STAGE_FINISHING); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC); + continue; + } + else + { + rym_process_params_init(RYM_STAGE_ESTABLISHING); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC); + continue; + } + + if (rym_tran_data(data_size, &rym_code) == 0) + { + if (rym_on_data != NULL) + rym_on_data(aPacketData + PACKET_DATA_INDEX, data_size); + + if (rym_code == RYM_CODE_CAN) + { + for (uint8_t i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++) + { + aPacketData[0] = RYM_CODE_CAN; + rym_transmit(aPacketData, 1); + } + } + else if (rym_code == RYM_CODE_ACK) + { + aPacketData[0] = RYM_CODE_ACK; + rym_transmit(aPacketData, 1); + } + } + } + } + } + FL_TAIL(fl); +} + +/** + * @brief Performs the finishing process for the YMODEM protocol. + * + * This function is responsible for handling the final stage of the YMODEM protocol, + * where the receiver receives the end of transmission (EOT) signal from the sender. + * It verifies the received EOT signal, sends an acknowledgement (ACK) signal back to + * the sender, and waits for the start of header (SOH) signal to receive the final + * packet containing the payload and checksum. If the received packet is valid, it + * calculates the checksum and compares it with the received checksum. If they match, + * it sets the stage to RYM_STAGE_FINISHED and invokes the callback function if + * available. This function also handles timeout conditions and reinitializes the + * protocol parameters if necessary. + * + * @param fl The flow structure pointer. + * @return The result of the finishing process. + */ +static uint8_t rym_do_finish_process(struct flow *fl) +{ + FL_HEAD(fl); + static uint16_t rym_recv_len; + static uint16_t recv_crc, cal_crc; + static uint16_t tran_timeout = 0; + + for (;;) + { + FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC); + if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem)) + { + if (tran_timeout++ >= 5) + { + tran_timeout = 0; + rym_process_params_init(RYM_STAGE_ESTABLISHING); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC); + } + } + else + { + tran_timeout = 0; + /* read the length of the packet */ + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1); + if (rym_recv_len == 1) + { + if (aPacketData[PACKET_START_INDEX] != RYM_CODE_EOT) + continue; + + /* send an ACK */ + aPacketData[0] = RYM_CODE_ACK; + rym_transmit(aPacketData, 1); + FL_LOCK_DELAY(fl, FL_CLOCK_100MSEC * 5); + /* send a C */ + aPacketData[0] = RYM_CODE_C; + rym_transmit(aPacketData, 1); + + FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, &msg_sem, FL_CLOCK_SEC); + if (FALSE == FL_SEM_IS_RELEASE(fl, &msg_sem)) + continue; + /* read the length of the packet */ + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_START_INDEX], 1); + if (rym_recv_len == 1) + { + if (aPacketData[PACKET_START_INDEX] != RYM_CODE_SOH) + continue; + + /* SOH/STX + seq + payload + crc */ + rym_recv_len = rym_sqqueue_read(&aPacketData[PACKET_NUMBER_INDEX], + _RYM_SOH_PKG_SZ - 1); + if (rym_recv_len != (_RYM_SOH_PKG_SZ - 1)) + continue; + /* sanity check */ + if ((aPacketData[PACKET_NUMBER_INDEX] != 0) || (aPacketData[PACKET_CNUMBER_INDEX] != 0xFF)) + continue; + recv_crc = (uint16_t)(*(&aPacketData[PACKET_START_INDEX] + _RYM_SOH_PKG_SZ - 2) << 8) | + *(&aPacketData[PACKET_START_INDEX] + _RYM_SOH_PKG_SZ - 1); + cal_crc = CRC16(aPacketData + PACKET_DATA_INDEX, _RYM_SOH_PKG_SZ - 5); + if (recv_crc != cal_crc) + continue; + + /* we got a valid packet. invoke the callback if there is one. */ + stage = RYM_STAGE_FINISHED; + aPacketData[0] = RYM_CODE_ACK; + rym_transmit(aPacketData, 1); + /* we already got one EOT in the caller. invoke the callback if there is + * one. */ + if (rym_on_end) + rym_on_end(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); + } + } + } + } + FL_TAIL(fl); +} + +/** + * @brief Process the Ymodem protocol stages. + * + * This function is responsible for handling the different stages of the Ymodem protocol. + * It performs the necessary actions based on the current stage. + * + * @note The stages include establishing connection, transmitting data, finishing, and others. + * + * @param None + * @return None + */ +void rym_process(void) +{ + switch (stage) + { + case RYM_STAGE_ESTABLISHING: // 建立连接 (Establishing connection) + rym_do_handshake_process(&handshake_fw); + break; + case RYM_STAGE_TRANSMITTING: // 传输中 (Transmitting) + rym_do_trans_process(&trans_fw); + break; + case RYM_STAGE_FINISHING: // 结束 (Finishing) + rym_do_finish_process(&finsh_fw); + break; + case RYM_STAGE_NONE: + rym_process_params_init(RYM_STAGE_ESTABLISHING); + break; + case RYM_STAGE_FINISHED: + rym_tm_sec = 0; + break; + default: + // rym_process_params_init(RYM_STAGE_ESTABLISHING); + break; + } +} + +/** + * @brief Checks if the YMODEM receive timeout has occurred. + * @return TRUE if the timeout has occurred, FALSE otherwise. + */ +BOOL rym_timeout(void) +{ + return rym_tm_sec == 0; +} + +/** + * @brief Initializes the YMODEM protocol for receiving files. + * + * This function initializes the necessary data structures and variables + * for receiving files using the YMODEM protocol. + * + * @return TRUE if initialization is successful, FALSE otherwise. + */ +BOOL rym_init(void) +{ + sqqueue_ctrl_init(&rym_sqqueue, sizeof(uint8_t), _RYM_PKG_SZ); + FL_INIT(&handshake_fw); + FL_INIT(&trans_fw); + FL_INIT(&finsh_fw); + return TRUE; +} + +/** + * @brief Receive data using the Ymodem protocol. + * + * This function is used to receive data transmitted using the Ymodem protocol and store it in the specified buffer. + * + * @param p Pointer to the data storage buffer. + */ +uint16_t rym_receive(void *p, uint16_t size) +{ + rym_sqqueue.string_enter(&rym_sqqueue, p, size); + FLOW_SEM_RELEASE(&msg_sem); + return 0; +} + +/** + * @brief Configures the YMODEM protocol with the specified callbacks and handshake timeout. + * + * This function sets the callback functions for the YMODEM protocol, which will be called during different stages of the protocol. + * The `on_begin` callback is called when the YMODEM transfer begins. + * The `on_data` callback is called when a data packet is received during the transfer. + * The `on_end` callback is called when the YMODEM transfer ends. + * The `on_transmit` callback is called when a data packet needs to be transmitted during the transfer. + * The `handshake_timeout` parameter specifies the timeout duration for the handshake phase of the YMODEM protocol. + * + * @param on_begin The callback function to be called when the YMODEM transfer begins. + * @param on_data The callback function to be called when a data packet is received during the transfer. + * @param on_end The callback function to be called when the YMODEM transfer ends. + * @param on_transmit The callback function to be called when a data packet needs to be transmitted during the transfer. + * @param handshake_timeout The timeout duration for the handshake phase of the YMODEM protocol. + * + * @return TRUE if the configuration was successful, FALSE otherwise. + */ +BOOL rym_config(rym_callback on_begin, rym_callback on_data, + rym_callback on_end, rym_callback on_transmit, + int handshake_timeout) +{ + rym_on_begin = on_begin; + rym_on_data = on_data; + rym_on_end = on_end; + rym_transmit = on_transmit; + tm_sec = handshake_timeout; + return TRUE; +} diff --git a/lib/bootload/ymodem.h b/lib/bootload/ymodem.h new file mode 100644 index 0000000..e6ce0f1 --- /dev/null +++ b/lib/bootload/ymodem.h @@ -0,0 +1,180 @@ +/** + * @file ymodem.h + * @author xsh + * @date 2024-02-18 19:32:46 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __YMODEM_H__ +#define __YMODEM_H__ + +#include "lib.h" + +enum rym_code +{ + RYM_CODE_NONE = 0x00, + RYM_CODE_SOH = 0x01, /* start of 128-byte data packet */ + RYM_CODE_STX = 0x02, /* start of 1024-byte data packet */ + RYM_CODE_EOT = 0x04, /* end of transmission */ + RYM_CODE_ACK = 0x06, /* acknowledge */ + RYM_CODE_NAK = 0x15, /* negative acknowledge */ + RYM_CODE_CAN = 0x18, /* two of these in succession aborts transfer */ + RYM_CODE_C = 0x43, /* 'C' == 0x43, request 16-bit CRC */ +}; +typedef enum rym_code rym_code_e; + +/* RYM error code + * + * We use the rt_err_t to return error values. We take use of current error + * codes available in RTT and append ourselves. + */ +/* timeout on handshake */ +#define RYM_ERR_TMO 0x70 +/* wrong code, wrong SOH, STX etc. */ +#define RYM_ERR_CODE 0x71 +/* wrong sequence number */ +#define RYM_ERR_SEQ 0x72 +/* wrong CRC checksum */ +#define RYM_ERR_CRC 0x73 +/* not enough data received */ +#define RYM_ERR_DSZ 0x74 +/* the transmission is aborted by user */ +#define RYM_ERR_CAN 0x75 + +/* how many ticks wait for chars between packet. */ +#ifndef RYM_WAIT_CHR_TICK +#define RYM_WAIT_CHR_TICK (OSEL_TICK_RATE_HZ * 3) +#endif +/* how many ticks wait for between packet. */ +#ifndef RYM_WAIT_PKG_TICK +#define RYM_WAIT_PKG_TICK (OSEL_TICK_RATE_HZ * 3) +#endif +/* how many ticks between two handshake code. */ +#ifndef RYM_CHD_INTV_TICK +#define RYM_CHD_INTV_TICK (OSEL_TICK_RATE_HZ * 3) +#endif + +/* how many CAN be sent when user active end the session. */ +#ifndef RYM_END_SESSION_SEND_CAN_NUM +#define RYM_END_SESSION_SEND_CAN_NUM 0x03 +#endif + +/* Exported constants --------------------------------------------------------*/ +/* Packet structure defines */ +#define PACKET_HEADER_SIZE ((uint32_t)3) +#define PACKET_DATA_INDEX ((uint32_t)4) +#define PACKET_START_INDEX ((uint32_t)1) +#define PACKET_NUMBER_INDEX ((uint32_t)2) +#define PACKET_CNUMBER_INDEX ((uint32_t)3) +#define PACKET_TRAILER_SIZE ((uint32_t)2) +#define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1) +#define PACKET_SIZE ((uint32_t)128) +#define PACKET_1K_SIZE ((uint32_t)1024) +#define _RYM_SOH_PKG_SZ (PACKET_SIZE + PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE) +#define _RYM_STX_PKG_SZ (PACKET_1K_SIZE + PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE) +#define _RYM_PKG_SZ _RYM_STX_PKG_SZ // 这里定义的是数据包的大小 + +/* 因为data是需要写入到flash里面,如果不对齐,会出现UNALIGNED异常 + * /-------- Packet in IAP memory ------------------------------------------\ + * | 0 | 1 | 2 | 3 | 4 | ... | n+4 | n+5 | n+6 | + * |------------------------------------------------------------------------| + * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 | + * \------------------------------------------------------------------------/ + * the first byte is left unused for memory alignment reasons */ + +#define FILE_NAME_LENGTH ((uint32_t)64) +#define FILE_SIZE_LENGTH ((uint32_t)16) + +#define NEGATIVE_BYTE ((uint8_t)0xFF) + +#define ABORT1 ((uint8_t)0x41) /* 'A' == 0x41, abort by user */ +#define ABORT2 ((uint8_t)0x61) /* 'a' == 0x61, abort by user */ + +#define MAX_ERRORS ((uint32_t)5) + +enum rym_stage +{ + RYM_STAGE_NONE, + /* set when C is send */ + RYM_STAGE_ESTABLISHING, + /* set when we've got the packet 0 and sent ACK and second C */ + RYM_STAGE_ESTABLISHED, + /* set when the sender respond to our second C and recviever got a real + * data packet. */ + RYM_STAGE_TRANSMITTING, + /* set when the sender send a EOT */ + RYM_STAGE_FINISHING, + /* set when transmission is really finished, i.e., after the NAK, C, final + * NULL packet stuff. */ + RYM_STAGE_FINISHED, +}; + +/* when receiving files, the buf will be the data received from ymodem protocol + * and the len is the data size. + * + * TODO: + * When sending files, the len is the buf size in RYM. The callback need to + * fill the buf with data to send. Returning RYM_CODE_EOT will terminate the + * transfer and the buf will be discarded. Any other return values will cause + * the transfer continue. + */ +typedef enum rym_code (*rym_callback)(uint8_t *buf, uint32_t len); + +/** recv a file on device dev with ymodem session ctx. + * + * If an error happens, you can get where it is failed from ctx->stage. + * + * @param on_begin The callback will be invoked when the first packet arrived. + * This packet often contain file names and the size of the file, if the sender + * support it. So if you want to save the data to a file, you may need to + * create the file on need. It is the on_begin's responsibility to parse the + * data content. The on_begin can be NULL, in which case the transmission will + * continue without any side-effects. + * + * @param on_data The callback will be invoked on the packets received. The + * callback should save the data to the destination. The return value will be + * sent to the sender and in turn, only RYM_{ACK,CAN} is valid. When on_data is + * NULL, RYM will barely send ACK on every packet and have no side-effects. + * + * @param on_end The callback will be invoked when one transmission is + * finished. The data should be 128 bytes of NULL. You can do some cleaning job + * in this callback such as closing the file. The return value of this callback + * is ignored. As above, this parameter can be NULL if you don't need such + * function. + * + * @param handshake_timeout the timeout when hand shaking. The unit is in + * second. + */ +BOOL rym_config(rym_callback on_begin, rym_callback on_data, + rym_callback on_end, rym_callback on_transmit, + int handshake_timeout); + +/** + * @brief Initializes the YMODEM protocol for receiving data. + * + * @return BOOL Returns TRUE if initialization is successful, FALSE otherwise. + */ +BOOL rym_init(void); + +/** + * @brief Receives data using the YMODEM protocol. + * + * @param p Pointer to the buffer where the received data will be stored. + * @param size The size of the buffer. + * @return uint16_t The number of bytes received. + */ +uint16_t rym_receive(void *p, uint16_t size); + +/** + * @brief Processes the received data using the YMODEM protocol. + */ +void rym_process(void); + +/** + * @brief Checks if a timeout has occurred during the YMODEM protocol. + * + * @return BOOL Returns TRUE if a timeout has occurred, FALSE otherwise. + */ +BOOL rym_timeout(void); + +#endif diff --git a/lib/control/inc/pid.h b/lib/control/inc/pid.h new file mode 100644 index 0000000..87b828c --- /dev/null +++ b/lib/control/inc/pid.h @@ -0,0 +1,235 @@ +#ifndef __PID_H__ +#define __PID_H__ +#include "lib.h" +#include "pid_auto_tune.h" + +#define INCOMPLETE_DIFFEREN 0 // 不完全微分 + +typedef enum +{ + PID_SUB_TYPE_POSITION = 1, // 位置式 + PID_SUB_TYPE_INCREMENT = 2, // 增量式 +} pid_sub_type_e; +typedef enum +{ + DEAD_ZONE_BOTH = 0, // 正负都可以 + DEAD_ZONE_POSITIVE = 1, // 正数 + DEAD_ZONE_NEGATIVE = 2, // 负数 +} deadzone_e; +typedef enum +{ + // PID自整定 + PID_TYPE_AUTO_TUNE, + // 通用PID + PID_TYPE_COMMON, + // 神经PID + PID_TYPE_NEURAL, + // 模糊PID + PID_TYPE_FUZZY, + + // 以下是自定义PID + + // cj PID + PID_TYPE_CUSTOM_CAO, + // gp jPID + PID_TYPE_CUSTOM_GAO, + // xsh PID + PID_TYPE_CUSTOM_XU, + // zxm PID + PID_TYPE_CUSTOM_ZHANG, + // hangdian PID + PID_TYPE_CUSTOM_HANGDIAN, +} pid_type_e; + +typedef struct +{ + float32 ref; + float32 feedback; + float32 pre_feedback; + float32 e_0; // 当前误差 + float32 e_1; // 上一次误差 + float32 kp; + float32 ki; + float32 kd; + float32 err_limit; + BOOL detach; + float32 err_dead; + float32 alpha; + float32 lastdev; + float32 out; + float32 out_max; + float32 out_min; + float32 sv_range; + float32 iout; // 积分输出 + BOOL sm; + BOOL ki_enable; + BOOL kd_enable; + float32 deviation; // 纠正系统误差造成的影响,作用于死区,大于0需要补偿,小于0需要反向补偿 + BOOL in_dead_zone; // 是否在死区内 +} pid_common_position_t; // 位置式PID + +typedef struct +{ + float32 ref; // 目标设定值 + float32 feedback; // 传感器采集值 + float32 out; // PID计算结果 + float32 kp; + float32 ki; + float32 kd; + float32 e_0; // 当前误差 + float32 e_1; // 上一次误差 + float32 e_2; // 上上次误差 + float32 err_dead; + float32 deviation; // 纠正系统误差造成的影响,作用于死区 + float32 out_max; // 输出限幅 + float32 out_min; // 输出限幅 + float32 sum_iterm; + float32 iout; // 积分输出 + float32 alpha; // 不完全微分参数 + float32 lastdev; // 不完全微分参数 + + BOOL sm; + BOOL ki_enable; + BOOL kd_enable; + float32 sv_range; + // 没有用的 + float32 err_limit; + BOOL detach; +} pid_common_increment_t; // 增量式PID + +typedef struct PID_COMMON +{ + uint8_t type; + + /* 设置PID三个参数 */ + void (*set_ctrl_prm)(struct PID_COMMON *self, float32 kp, float32 ki, float32 kd); + /* 设置积分范围 */ + void (*set_integral_prm)(struct PID_COMMON *self, float32 integral_up, float32 integral_low); + + /* 控制接口 */ + float32 (*PID)(struct PID_COMMON *self, float32 err); + + /* in value */ + float32 err; + /* out value */ + float32 out; + + union + { + pid_common_position_t position; + pid_common_increment_t increment; + } pri_u; + +} pid_common_t; // 通用PID + +typedef struct PID_NEURAL +{ + uint8_t type; + /* 设置PID三个参数 */ + void (*set_ctrl_prm)(struct PID_NEURAL *self, float32 minimum, float32 maximum); + /* 设置输出范围 */ + void (*set_out_prm)(struct PID_NEURAL *self, float32 minimum, float32 maximum); + /* 控制接口 */ + float32 (*PID)(struct PID_NEURAL *self, float32 target, float32 feedback); + + struct + { + float32 setpoint; /*设定值*/ + float32 kcoef; /*神经元输出比例*/ + float32 kp; /*比例学习速度*/ + float32 ki; /*积分学习速度*/ + float32 kd; /*微分学习速度*/ + float32 lasterror; /*前一拍偏差*/ + float32 preerror; /*前两拍偏差*/ + float32 deadband; /*死区*/ + float32 result; /*输出值*/ + float32 output; /*百分比输出值*/ + float32 maximum; /*输出值的上限*/ + float32 minimum; /*输出值的下限*/ + float32 wp; /*比例加权系数*/ + float32 wi; /*积分加权系数*/ + float32 wd; /*微分加权系数*/ + } pri; +} pid_neural_t; // 神经PID + +typedef struct +{ + float32 kp; + float32 ki; + float32 kd; + + float32 kup; + float32 kui; + float32 kud; + + float32 maxe; // 非线性区间最大值 + float32 mine; // 非线性区间最小值 +} FUZZY_PID_t; + +// 模糊PID +typedef struct PID_FUZZY +{ + /* 设置PID三个参数 */ + void (*set_ctrl_prm)(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 deviation, + float32 out_min, float32 out_max); // 设置PID参数 + void (*set_error_max_min)(struct PID_FUZZY *self, float32 mine, float32 maxe); // 设置非线性区间值 + void (*update_ctrl_prm)(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, + float32 out_min, float32 out_max); // 更新PID参数 + void (*set_range)(struct PID_FUZZY *self, float32 out_min, float32 out_max); // 更新最大最小值 + void (*set_cfg)(struct PID_FUZZY *self, float32 max_err, BOOL mode); // 配置PID模式,默认不使用积分分离 + void (*set_smooth_enable)(struct PID_FUZZY *self, BOOL enable, float32 sv_range); // 设置平滑范围 + void (*set_iout)(struct PID_FUZZY *self, float32 iout); // 设置积分输出 + void (*set_err_dead)(struct PID_FUZZY *self, float32 err_dead); // 设置死区 + void (*set_kp)(struct PID_FUZZY *self, float32 kp); + void (*set_ki_enable)(struct PID_FUZZY *self, BOOL enable); + void (*set_ki)(struct PID_FUZZY *self, float32 ki); + // 微分开启使能 + void (*set_kd_enable)(struct PID_FUZZY *self, BOOL enable); + void (*set_kd)(struct PID_FUZZY *self, float32 kd); + void (*set_kd_dev)(struct PID_FUZZY *self, float32 alpha); + void (*restctrl)(struct PID_FUZZY *self, float32 out); // 复位PID积分及微分控制数据 + /* 控制接口 */ + float32 (*execute)(struct PID_FUZZY *self, float32 target, float32 feedback); + + BOOL(*in_dead_zone) + (struct PID_FUZZY *self); + + union + { + pid_common_position_t position; + pid_common_increment_t increment; + } pri_u; + + pid_sub_type_e sub_type; // 位置式PID,增量式PID + BOOL open; // 是否使用模糊PID控制 + BOOL speed_integral_enable; // 变速积分,暂时没有验证成功 + deadzone_e deadzone_dir; + FUZZY_PID_t pid_params; + +} pid_fuzzy_t; // 模糊PID + +// PID +typedef struct +{ + BOOL is_init; // 是否初始化 + pid_type_e type; // 不同的算法类型,模糊PID,神经PID,通用PID + pid_sub_type_e sub_type; // 位置式PID,增量式PID + union + { + pid_common_t common; + pid_neural_t neural; + pid_fuzzy_t fuzzy; + } pid_u; + pid_auto_tune_t auto_tune; +} pid_t; + +// PID控制 +extern void pid_constructor(pid_t *self); + +// private +// 神经元PID +extern void pid_neural_constructor(struct PID_NEURAL *self); +// 模糊PID +extern void pid_fuzzy_constructor(struct PID_FUZZY *self); + +#endif diff --git a/lib/control/inc/pid_auto_tune.h b/lib/control/inc/pid_auto_tune.h new file mode 100644 index 0000000..e9e701c --- /dev/null +++ b/lib/control/inc/pid_auto_tune.h @@ -0,0 +1,53 @@ +/*** + * @Author: + * @Date: 2023-07-24 11:17:55 + * @LastEditors: xxx + * @LastEditTime: 2023-07-24 11:19:06 + * @Description:pid自动调参,构建闭环回路 确定稳定极限 确定两个参数 极限值KP和震荡周期 + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#ifndef __PID_AUTO_TUNE_H__ +#define __PID_AUTO_TUNE_H__ +#include "lib.h" + +typedef struct PID_AUTO_TUNE +{ + // public: + void (*set_ctrl_prm)(struct PID_AUTO_TUNE *self, float32 *input, float32 *output); + int32_t (*runtime)(struct PID_AUTO_TUNE *self); + void (*set_output_step)(struct PID_AUTO_TUNE *self, int32_t step); + void (*set_control_type)(struct PID_AUTO_TUNE *self, int32_t type); + void (*set_noise_band)(struct PID_AUTO_TUNE *self, int32_t band); + void (*set_look_back)(struct PID_AUTO_TUNE *self, int32_t n); + float32 (*get_kp)(struct PID_AUTO_TUNE *self); + float32 (*get_ki)(struct PID_AUTO_TUNE *self); + float32 (*get_kd)(struct PID_AUTO_TUNE *self); + // private: + struct + { + BOOL isMax, isMin; // 运算中出现最大、最小值标志 + float32 *input, *output; + float32 setpoint; // 反向控制判断值,这个值需要根据对象的实际工作值确定!是通过第一次启动时对应的输入值带入的。 + int32_t noiseBand; // 判断回差,类似于施密特触发器,实际控制反向的比较值是 setpoint + noiseBand 或 setpoint - noiseBand + int32_t controlType; // 计算 PID 参数时,选择 PI 或 PID 模式,输出 Kp Ki,或 Kp、Ki、Kd + BOOL running; + uint32_t peak1, peak2, lastTime; // 峰值对应的时间 + int32_t sampleTime; + int32_t nLookBack; + int32_t peakType; + int32_t lastInputs[51]; // 保存的历史输入值, 改为 50 次。 by shenghao.xu + int32_t peaks[13]; // 保存的历史峰值,最多存前 12 次,对应 6个最大、6个最小。20221124 by Embedream + int32_t peakCount; // 峰值计数 + int32_t peakPeriod[7]; // 保存前 6 次的最大值间隔时间 by shenghao.xu + int32_t peakMaxCount; // 最大峰值计数 by shenghao.xu + BOOL justchanged; + int32_t oStep; // 这个值是用于计算控制高低值的,以 outputStart 为中值,输出高值用 outputStart + oStep, 输出低值用 outputStart - oStep + float32 outputStart; // 输出控制的基础值,这个需要结合对象特征确定,此值也是通过第一次启动时对应的输出值带入的。 + float32 Ku, Pu; + } pri; + +} pid_auto_tune_t; + +extern void pid_auto_tune_constructor(struct PID_AUTO_TUNE *self); +#endif // __PID_AUTO_TUNE_H__ diff --git a/lib/control/inc/s_curve.h b/lib/control/inc/s_curve.h new file mode 100644 index 0000000..4f2a419 --- /dev/null +++ b/lib/control/inc/s_curve.h @@ -0,0 +1,30 @@ +#ifndef __S_CURVE_H +#define __S_CURVE_H +#include "lib.h" + +typedef struct +{ + // 起始频率 + float32 f0; + // 加加速段斜率 + float32 faa; + // 减减速段斜率 + float32 frr; + // 加加速段时间 + float32 taa; + // 匀加速段时间 + float32 tua; + // 减加速段时间 + float32 tra; + // 匀速段时间 + float32 tuu; + // 加减速段时间 + float32 tar; + // 匀减速段时间 + float32 tur; + // 减减速段时间 + float32 trr; +} s_curve_t; + +void s_curve_table_gen(uint16_t tmin, uint16_t tmax); +#endif // __S_CURVE_H diff --git a/lib/control/src/pid.c b/lib/control/src/pid.c new file mode 100644 index 0000000..e2cc21f --- /dev/null +++ b/lib/control/src/pid.c @@ -0,0 +1,27 @@ +#include "pid.h" +#include + +// 构造函数将接口绑定 +void pid_constructor(pid_t *self) +{ + switch (self->type) + { + case PID_TYPE_COMMON: + /* code */ + break; + case PID_TYPE_NEURAL: + pid_neural_constructor(&self->pid_u.neural); + break; + case PID_TYPE_FUZZY: + DBG_ASSERT(self->sub_type != 0 __DBG_LINE); + self->pid_u.fuzzy.sub_type = self->sub_type; + pid_fuzzy_constructor(&self->pid_u.fuzzy); + break; + case PID_TYPE_AUTO_TUNE: + pid_auto_tune_constructor(&self->auto_tune); + break; + default: + break; + } + self->is_init = TRUE; +} diff --git a/lib/control/src/pid_auto_tune.c b/lib/control/src/pid_auto_tune.c new file mode 100644 index 0000000..5204421 --- /dev/null +++ b/lib/control/src/pid_auto_tune.c @@ -0,0 +1,230 @@ +#include "pid_auto_tune.h" +#include "sys.h" + +/* + 设置峰值回溯时间,单位 0.1 秒,最小 0.2秒, 最大 4 秒 +*/ +static void set_look_backsec(pid_auto_tune_t *self, int32_t value) +{ + if (value < 2) + value = 2; + if (value > 40) + value = 40; + + if (value < 40) + { + self->pri.nLookBack = 12; // 按目前实际周期约300ms、采样周期 10ms 考虑,一个周期只有 30 点,回溯 12 点即可。 + self->pri.sampleTime = value * 10; // 改为 Value*10 ms, 20、30、40 ~ 200ms + } + else + { + self->pri.nLookBack = 50 + value; + self->pri.sampleTime = 4000; + } +} + +static void _set_ctrl_prm(struct PID_AUTO_TUNE *self, float32 *input, float32 *output) +{ + self->pri.input = input; + self->pri.output = output; + self->pri.controlType = 0; // 默认为 PI 模式 + self->pri.noiseBand = 1; + self->pri.running = FALSE; + self->pri.oStep = 1; + set_look_backsec(self, 8); + self->pri.lastTime = sys_millis(); +} + +static void _set_noise_band(struct PID_AUTO_TUNE *self, int32_t value) +{ + self->pri.noiseBand = value; +} + +static void _set_output_step(struct PID_AUTO_TUNE *self, int32_t value) +{ + self->pri.oStep = value; +} + +// * Determies if the tuning parameters returned will be PI (D=0) +// or PID. (0=PI, 1=PID) +static void _set_control_type(struct PID_AUTO_TUNE *self, int32_t value) +{ + self->pri.controlType = value; +} + +static void _set_look_back(struct PID_AUTO_TUNE *self, int32_t value) +{ + set_look_backsec(self, value); +} + +static float32 _get_kp(struct PID_AUTO_TUNE *self) +{ + float32 kp = self->pri.controlType == 1 ? 0.6f * self->pri.Ku : 0.4f * self->pri.Ku; + return kp; +} + +static float32 _get_ki(struct PID_AUTO_TUNE *self) +{ + float32 ki = self->pri.controlType == 1 ? 1.2f * self->pri.Ku / self->pri.Pu : 0.48f * self->pri.Ku / self->pri.Pu; + return ki; +} + +static float32 _get_kd(struct PID_AUTO_TUNE *self) +{ + return self->pri.controlType == 1 ? 0.075f * self->pri.Ku * self->pri.Pu : 0; +} + +/** + * @brief 修改返回值,0 - 执行计算,未完成整定, 1 - 执行计算,完成整定过程, 2 - 采样时间未到 + * @return {*} + */ +static int32_t _runtime(struct PID_AUTO_TUNE *self) +{ + int32_t i, iSum; + + uint32_t now = sys_millis(); + if ((now - self->pri.lastTime) < ((uint32_t)self->pri.sampleTime)) + { + return 2; // 原来返回值为 FALSE 不符合函数定义,也无法区分,改为 2,by shenghao.xu + } + + // 开始整定计算 + self->pri.lastTime = now; + float32 refVal = *(self->pri.input); + if (FALSE == self->pri.running) // 首次进入,初始化参数 + { + self->pri.peakType = 0; + self->pri.peakCount = 0; + self->pri.peakMaxCount = 0; + self->pri.peak1 = 0; + self->pri.peak2 = 0; + self->pri.justchanged = FALSE; + self->pri.setpoint = refVal; // 不变 + self->pri.running = TRUE; + self->pri.outputStart = *self->pri.output; + *self->pri.output = self->pri.outputStart + self->pri.oStep; + } + + // 根据输入与设定点的关系振荡输出 + if (refVal > (self->pri.setpoint + self->pri.noiseBand)) + *self->pri.output = self->pri.outputStart - self->pri.oStep; + else if (refVal < (self->pri.setpoint - self->pri.noiseBand)) + *self->pri.output = self->pri.outputStart + self->pri.oStep; + + // bool isMax=TRUE, isMin=TRUE; + self->pri.isMax = TRUE; + self->pri.isMin = TRUE; + // id peaks + /* + 以下循环完成,对回溯次数的输入缓存进行判断,如果输入值均大于或小于缓存值,则确定此次为峰值。 + 峰值特征根据 isMax、isMin 哪个为真确定。 + 同时完成输入缓存向后平移,腾出第一个单元存放新的输入值。 + 这一段代码完成的噪声所产生的虚假峰值判断,应该没有问题! + */ + for (i = self->pri.nLookBack - 1; i >= 0; i--) + { + int32_t val = self->pri.lastInputs[i]; + if (self->pri.isMax) + self->pri.isMax = (refVal > val); // 第一次是新输入和缓存最后一个值比较,如果大于,则前面的值均判是否大于 + if (self->pri.isMin) + self->pri.isMin = (refVal < val); // 第一次是新输入和缓存最后一个值比较,如果小于,则前面的值均判是否小于 + self->pri.lastInputs[i + 1] = self->pri.lastInputs[i]; // 每采样一次,将输入缓存的数据向后挪一次 + } + self->pri.lastInputs[0] = refVal; // 新采样的数据放置缓存第一个单元。 + + /* + 以下代码完成峰值的确定,以及对应峰值的时间纪录。 + 因为上述代码只是去掉噪产生的波动峰值,但如果是连续超过 nLookBack 次数的的上升或下降, + 则上述算法所确定的最大或最小值,并非是峰值,只能是前 nLookBack 次中的最大或最小值。 + 但逐句消化程序后,发现这段处理有几点疑惑: + 1、peaks[] 的纪录好像不对,在执行最小到最大值转换时,peakCount 也应该+1,否则应该把 + 纪录的最小值覆盖了!所以后面的峰值判断总是满足条件。 + 2、峰值对应时间似乎也应该多次存放,取平均值,因对象没有那么理想化,目前应该是取的最后一组峰值的周期。 + 3、后续计算 Ku 用的是整个整定过程的最大、最小值,这对于非理想的对象而言也不是很合适。 + + 考虑做如下改进: + 1)修改峰值纪录,设计12个峰值保存单元,存满12个峰值(6大、6小)后再计算。 + 2)纪录 6 组最大值的间隔时间,作为最终计算 Pu 的数据。 + */ + if (self->pri.isMax) + { + if (self->pri.peakType == 0) + self->pri.peakType = 1; // 首次最大值,初始化 + + if (self->pri.peakType == -1) // 如果前一次为最小值,则标识目前进入最大值判断 + { + self->pri.peakType = 1; // 开始最大值判断 + self->pri.peakCount++; // 峰值计数 by shenghao.xu + self->pri.justchanged = TRUE; // 标识峰值转换 + if (self->pri.peak2 != 0) // 已经纪录一次最大峰值对应时间后,开始记录峰值周期 by shenghao.xu + { + self->pri.peakPeriod[self->pri.peakMaxCount] = (int32_t)(self->pri.peak1 - self->pri.peak2); // 最大峰值间隔时间(即峰值周期) + self->pri.peakMaxCount++; // 最大峰值计数 + } + self->pri.peak2 = self->pri.peak1; // 刷新上次最大值对应时间 + } + self->pri.peak1 = now; // 保存最大值对应时间 peak1 + self->pri.peaks[self->pri.peakCount] = refVal; // 保存最大值 + } // 此段代码可以保证得到的是真正的最大值,因为peakType不变,则会不断刷新最大值 + else if (self->pri.isMin) + { + if (self->pri.peakType == 0) + self->pri.peakType = -1; // 首次最小值,初始化 + + if (self->pri.peakType == 1) // 如果前一次是最大值判断,则转入最小值判断 + { + self->pri.peakType = -1; // 开始最小值判断 + self->pri.peakCount++; // 峰值计数 + self->pri.justchanged = TRUE; + } + + if (self->pri.peakCount < 10) + self->pri.peaks[self->pri.peakCount] = refVal; // 只要类型不变,就不断刷新最小值 + } + + /* by shenghao.xu + 以下计算是作为判断采集数据是否合适的部分,如果 2 次峰值判断条件满足,就结束整定过程,感觉不甚合理。 + 拟修改为: + 1)计满 12 次峰值后再计算(到第 13 次)。 + 2)不再判断是否合理,因为对象如果特性好,自然已经稳定,如果不好,再长时间也无效果。 + 3)将后面5次的数据作为素材,去掉第一组数据,因为考虑第一组时对象可能处于过渡过程。 + 4)用后 10 点得到的 9 个峰值差平均值作为 Ku 计算值中的 A,取代原来的整个过程的最大、最小值差。 + 5)用后 5 点峰值周期平均值作为 Pu 的计算值,取代原来用最后一组的值。 + */ + if (self->pri.justchanged && self->pri.peakCount == 12) + { + // we've transitioned. check if we can autotune based on the last peaks + iSum = 0; + for (i = 2; i <= 10; i++) + iSum += ABS(self->pri.peaks[i] - self->pri.peaks[i + 1]); + iSum /= 9; // 取 9 次峰峰值平均值 + self->pri.Ku = (float32)(4 * (2 * self->pri.oStep)) / (iSum * 3.14159); // 用峰峰平均值计算 Ku + + iSum = 0; + for (i = 1; i <= 5; i++) + iSum += self->pri.peakPeriod[i]; + iSum /= 5; // 计算峰值的所有周期平均值 + self->pri.Pu = (float32)(iSum) / 1000; // 用周期平均值作为 Pu,单位:秒 + + *self->pri.output = 0; + self->pri.running = FALSE; + return 1; + } + + self->pri.justchanged = FALSE; + return 0; +} + +void pid_auto_tune_constructor(struct PID_AUTO_TUNE *self) +{ + self->set_ctrl_prm = _set_ctrl_prm; + self->runtime = _runtime; + self->set_output_step = _set_output_step; + self->set_control_type = _set_control_type; + self->set_noise_band = _set_noise_band; + self->set_look_back = _set_look_back; + + self->get_kp = _get_kp; + self->get_ki = _get_ki; + self->get_kd = _get_kd; +} diff --git a/lib/control/src/pid_common.c b/lib/control/src/pid_common.c new file mode 100644 index 0000000..e69de29 diff --git a/lib/control/src/pid_fuzzy.c b/lib/control/src/pid_fuzzy.c new file mode 100644 index 0000000..2d73187 --- /dev/null +++ b/lib/control/src/pid_fuzzy.c @@ -0,0 +1,894 @@ +#include "pid.h" +#include +// 定义死区枚举 + +#define DEADZONE DEAD_ZONE_POSITIVE +// 模糊集合 +#define NL -3 +#define NM -2 +#define NS -1 +#define ZE 0 +#define PS 1 +#define PM 2 +#define PL 3 + +// 定义偏差E的范围,因为设置了非线性区间,误差在10时才开始进行PID调节,这里E的范围为10 +#define MAXE (100) +#define MINE (-MAXE) +// 定义EC的范围,因为变化非常缓慢!,每次的EC都非常小,这里可以根据实际需求来调整, +#define MAXEC (100) +#define MINEC (-MAXEC) +// 定义e,ec的量化因子 +#define KE 3 / MAXE +#define KEC 3 / MAXEC + +// 定义输出量比例因子 +#define KUP 1.0f // 这里只使用了模糊PID的比例增益 +#define KUI 0.0f +#define KUD 0.0f + +static const float32 fuzzyRuleKp[7][7] = { + PL, PL, PM, PL, PS, PM, PL, + PL, PM, PM, PM, PS, PM, PL, + PM, PS, PS, PS, PS, PS, PM, + PM, PS, ZE, ZE, ZE, PS, PM, + PS, PS, PS, PS, PS, PM, PM, + PM, PM, PM, PM, PL, PL, PL, + PM, PL, PL, PL, PL, PL, PL}; + +static const float32 fuzzyRuleKi[7][7] = { + NL, NL, NL, NL, NM, NL, NL, + NL, NL, NM, NM, NM, NL, NL, + NM, NM, NS, NS, NS, NM, NM, + NM, NS, ZE, ZE, ZE, NS, NM, + NM, NS, NS, NS, NS, NM, NM, + NM, NM, NS, NM, NM, NL, NL, + NM, NL, NM, NL, NL, NL, NL}; + +static const float32 fuzzyRuleKd[7][7] = { + PS, PS, ZE, ZE, ZE, PL, PL, + NS, NS, NS, NS, ZE, NS, PM, + NL, NL, NM, NS, ZE, PS, PM, + NL, NM, NM, NS, ZE, PS, PM, + NL, NM, NS, NS, ZE, PS, PS, + NM, NS, NS, NS, ZE, PS, PS, + PS, ZE, ZE, ZE, ZE, PL, PL}; + +static void fuzzy(float32 e, float32 ec, FUZZY_PID_t *fuzzy_pid) +{ + + float32 etemp, ectemp; + float32 eLefttemp, ecLefttemp; // ec,e,左隶属度 + float32 eRighttemp, ecRighttemp; + + int eLeftIndex, ecLeftIndex; // 模糊位置标号 + int eRightIndex, ecRightIndex; + e = RANGE(e, fuzzy_pid->mine, fuzzy_pid->maxe); + ec = RANGE(ec, MINEC, MAXEC); + e = e * KE; + ec = ec * KEC; + + etemp = e > 3.0f ? 0.0f : (e < -3.0f ? 0.0f : (e >= 0.0f ? (e >= 2.0f ? 2.5f : (e >= 1.0f ? 1.5f : 0.5f)) : (e >= -1.0f ? -0.5f : (e >= -2.0f ? -1.5f : (e >= -3.0f ? -2.5f : 0.0f))))); + eLeftIndex = (int)((etemp - 0.5f) + 3); //[-3,3] -> [0,6] + eRightIndex = (int)((etemp + 0.5f) + 3); + eLefttemp = etemp == 0.0f ? 0.0f : ((etemp + 0.5f) - e); // + eRighttemp = etemp == 0.0f ? 0.0f : (e - (etemp - 0.5f)); + ectemp = ec > 3.0f ? 0.0f : (ec < -3.0f ? 0.0f : (ec >= 0.0f ? (ec >= 2.0f ? 2.5f : (ec >= 1.0f ? 1.5f : 0.5f)) : (ec >= -1.0f ? -0.5f : (ec >= -2.0f ? -1.5f : (ec >= -3.0f ? -2.5f : 0.0f))))); + ecLeftIndex = (int)((ectemp - 0.5f) + 3); //[-3,3] -> [0,6] + ecRightIndex = (int)((ectemp + 0.5f) + 3); + + ecLefttemp = ectemp == 0.0f ? 0.0f : ((ectemp + 0.5f) - ec); + ecRighttemp = ectemp == 0.0f ? 0.0f : (ec - (ectemp - 0.5f)); + + /*************************************反模糊*************************************/ + + fuzzy_pid->kp = (eLefttemp * ecLefttemp * fuzzyRuleKp[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKp[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKp[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKp[eRightIndex][ecRightIndex]); + + fuzzy_pid->ki = (eLefttemp * ecLefttemp * fuzzyRuleKi[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKi[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKi[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKi[eRightIndex][ecRightIndex]); + + fuzzy_pid->kd = (eLefttemp * ecLefttemp * fuzzyRuleKd[eLeftIndex][ecLeftIndex] + eLefttemp * ecRighttemp * fuzzyRuleKd[eLeftIndex][ecRightIndex] + eRighttemp * ecLefttemp * fuzzyRuleKd[eRightIndex][ecLeftIndex] + eRighttemp * ecRighttemp * fuzzyRuleKd[eRightIndex][ecRightIndex]); + // 对解算出的KP,KI,KD进行量化映射 + + fuzzy_pid->kp = fuzzy_pid->kp * fuzzy_pid->kup; + fuzzy_pid->ki = fuzzy_pid->ki * fuzzy_pid->kui; + fuzzy_pid->kd = fuzzy_pid->kd * fuzzy_pid->kud; +} + +/** + * @brief SV平滑给定,步长默认为0.1,范围0-1之间,越大平滑性越差 + * @param {PID_FUZZY} *self + * @param {float32} target_sv + * @return {*} + * @note + */ +static void smooth_setpoint(struct PID_FUZZY *self, float32 target_sv) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + float32 stepIn = (pri->sv_range) * 0.1f; + float32 kFactor = 0.0f; + if (fabs(pri->ref - target_sv) <= stepIn) + { + pri->ref = target_sv; + } + else + { + if (pri->ref - target_sv > 0) + { + kFactor = -1.0f; + } + else if (pri->ref - target_sv < 0) + { + kFactor = 1.0f; + } + else + { + kFactor = 0.0f; + } + pri->ref = pri->ref + kFactor * stepIn; + } + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + float32 stepIn = (pri->sv_range) * 0.1f; + float32 kFactor = 0.0f; + if (fabs(pri->ref - target_sv) <= stepIn) + { + pri->ref = target_sv; + } + else + { + if (pri->ref - target_sv > 0) + { + kFactor = -1.0f; + } + else if (pri->ref - target_sv < 0) + { + kFactor = 1.0f; + } + else + { + kFactor = 0.0f; + } + pri->ref = pri->ref + kFactor * stepIn; + } + } +} + +// 变速积分 +static float32 changing_integral_rate(struct PID_FUZZY *self) +{ + float32 err = 0, iout = 0; + float32 err_1 = 1, // 误差下限 + err_2 = 10; // 误差上限 + float32 index = 0; + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + err = pri->e_0; + iout = pri->iout; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + err = pri->e_0; + iout = pri->iout; + } + + if (err * iout > 0) // 判断积分是否为积累趋势 + { + if (ABS(err) <= err_1) + { + index = 1; // 完整积分 + } + else if (ABS(err) <= (err_1 + err_2)) + { + // 使用线性函数过渡 + index = (float)(err_2 - ABS(err) + err_1) / err_2; + } + else + { + index = 0; + } + } + + return index; +} + +/*封装模糊接口*/ +static void compensate(float32 e, float32 ec, FUZZY_PID_t *fuzzy_d) +{ + fuzzy(e, ec, fuzzy_d); +} + +/** + * @brief 更新最大最小值 + * @param {PID_FUZZY} *self + * @param {float32} out_min + * @param {float32} out_max + * @return {*} + * @note + */ +static void _set_range(struct PID_FUZZY *self, float32 out_min, float32 out_max) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->out_max = out_max; + pri->out_min = out_min; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->out_max = out_max; + pri->out_min = out_min; + } +} + +/** + * @brief 设置死区 + * @param {PID_FUZZY} *self + * @param {float32} err_dead + * @return {*} + * @note + */ +static void _set_err_dead(struct PID_FUZZY *self, float32 err_dead) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->err_dead = err_dead; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->err_dead = err_dead; + } +} + +static void _set_iout(struct PID_FUZZY *self, float32 iout) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->iout = iout; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->iout = iout; + } +} + +static void _set_kp(struct PID_FUZZY *self, float32 kp) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->kp = kp; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->kp = kp; + } +} + +/** + * @brief 使能积分控制 + * @param {PID_FUZZY} *self + * @param {BOOL} enable + * @return {*} + * @note + */ +// static void _set_ki_enable(struct PID_FUZZY *self, BOOL enable) +// { +// pri->ki_enable = enable; +// } + +/** + * @brief 使能微分控制 + * @param {PID_FUZZY} *self + * @param {BOOL} enable + * @return {*} + * @note + */ +static void _set_kd_enable(struct PID_FUZZY *self, BOOL enable) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->kd_enable = enable; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->kd_enable = enable; + } +} + +static void _set_kd(struct PID_FUZZY *self, float32 kd) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->kd = kd; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->kd = kd; + } +} + +/** + * @brief 配置不完全微分系数 + * @param {PID_FUZZY} *self + * @param {float32} alpha + * @return {*} + * @note alpha范围0-1,系数越大,不完全微分的作用越强 + */ +static void _set_kd_dev(struct PID_FUZZY *self, float32 alpha) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->alpha = alpha; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->alpha = alpha; + } +} + +static void _set_ki_enable(struct PID_FUZZY *self, BOOL enable) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->ki_enable = enable; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->ki_enable = enable; + } +} + +static void _set_ki(struct PID_FUZZY *self, float32 ki) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->ki = ki; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->ki = ki; + } +} + +/* + * Function:使能平滑控制 + * parameter:*pid需要配,PID参数结构指针,sv_range控制范围sv的范围 + * return:无 + */ +static void _set_smooth_enable(struct PID_FUZZY *self, BOOL enable, float32 sv_range) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->sm = enable; + pri->sv_range = sv_range; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->sm = enable; + pri->sv_range = sv_range; + } +} + +// 设置控制参数 +static void _set_ctrl_prm(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, float32 deviation, + float32 out_min, float32 out_max) +{ + self->open = TRUE; + self->pid_params.kup = KUP; + self->pid_params.kui = KUI; + self->pid_params.kud = KUD; + self->pid_params.mine = MINE; + self->pid_params.maxe = MAXE; + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + osel_memset((uint8_t *)pri, 0, sizeof(pid_common_position_t)); + pri->kp = kp; + pri->ki = ki; + pri->kd = kd; + pri->err_dead = err_dead; + pri->deviation = deviation; + pri->out_max = out_max; + pri->out_min = out_min; + pri->detach = FALSE; + pri->sm = FALSE; + pri->ki_enable = TRUE; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + osel_memset((uint8_t *)pri, 0, sizeof(pid_common_increment_t)); + pri->kp = kp; + pri->ki = ki; + pri->kd = kd; + pri->err_dead = err_dead; + pri->deviation = deviation; + pri->out_max = out_max; + pri->out_min = out_min; + pri->ki_enable = TRUE; + } +} + +static void _set_error_max_min(struct PID_FUZZY *self, float32 mine, float32 maxe) +{ + self->pid_params.mine = mine; + self->pid_params.maxe = maxe; +} + +static void _update_ctrl_prm(struct PID_FUZZY *self, float32 kp, float32 ki, float32 kd, float32 err_dead, + float32 out_min, float32 out_max) +{ + + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->kp = kp; + pri->ki = ki; + pri->kd = kd; + pri->err_dead = err_dead; + pri->out_max = out_max; + pri->out_min = out_min; + pri->detach = FALSE; + pri->sm = FALSE; + + if (kd > 0) + { + pri->kd_enable = TRUE; + } + else + { + pri->kd_enable = FALSE; + } + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->kp = kp; + pri->ki = ki; + pri->kd = kd; + pri->err_dead = err_dead; + pri->out_max = out_max; + pri->out_min = out_min; + + if (kd > 0) + { + pri->kd_enable = TRUE; + } + else + { + pri->kd_enable = FALSE; + } + } +} + +/** + * @brief 非0时配置为积分分离+抗积分饱和PID,否则为普通抗积分饱和PID + * @param {PID_FUZZY} *self + * @param {float32} max_err + * @param {BOOL} mode + * @return {*} + */ +static void _set_cfg(struct PID_FUZZY *self, float32 max_err, BOOL mode) +{ + + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->err_limit = max_err; + pri->detach = mode == FALSE ? FALSE : TRUE; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->err_limit = max_err; + pri->detach = mode == FALSE ? FALSE : TRUE; + } +} + +/** + * @brief 判断是否处于死区范围内 + * + * 根据给定的 PID_FUZZY 结构体,判断当前值是否处于死区范围内。 + * + * @param self PID_FUZZY 结构体指针 + * + * @return 如果处于死区范围内,返回 TRUE;否则返回 FALSE + */ +static BOOL _in_dead_zone(struct PID_FUZZY *self) +{ + float32 deviation = 0.0f; + float32 err_dead = 0.0f; + float32 err = 0.0f; + float32 offset = 0.0f; + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + deviation = pri->deviation; + err_dead = pri->err_dead; + err = pri->feedback - pri->ref; + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + deviation = pri->deviation; + err_dead = pri->err_dead; + err = pri->feedback - pri->ref; + } + + offset = err + deviation; + + if (self->deadzone_dir == DEAD_ZONE_POSITIVE) + { + if (offset >= 0 && offset <= ABS(err_dead)) + { + return TRUE; + } + else + { + return FALSE; + } + } + else if (self->deadzone_dir == DEAD_ZONE_NEGATIVE) + { + if (offset <= 0 && offset >= err_dead) + { + return TRUE; + } + else + { + return FALSE; + } + } + else + { + if (ABS(offset) <= ABS(err_dead)) + { + return TRUE; + } + else + { + return FALSE; + } + } +} + +static float32 position_pid(struct PID_FUZZY *self, float32 target, float32 feedback) +{ + float32 error = 0; + float32 ec = 0; + float32 kd = 0; + float32 thisdev = 0; + + pid_common_position_t *pri = &self->pri_u.position; + kd = pri->kd; + /*获取期望值与实际值,进行偏差计算*/ + if (pri->sm == 1) + { + smooth_setpoint(self, target); + } + else + { + pri->ref = target; + } + + pri->feedback = feedback; + error = pri->ref - pri->feedback; + + if (self->in_dead_zone(self) == TRUE) + { + error = 0; + pri->in_dead_zone = TRUE; + } + else + { + pri->in_dead_zone = FALSE; + } + + pri->e_0 = error; + + /* fuzzy control caculate */ + ec = error - pri->e_1; + if (self->open == TRUE) + { + compensate(error, ec, &self->pid_params); + } + + /*根据PID配置的模式,获取积分数据,进行积分累加*/ + if (self->speed_integral_enable == TRUE) + { + pri->iout = (pri->ki + self->pid_params.ki) * error * changing_integral_rate(self); + } + else + { + float32 temp_iterm = 0.0f; + float32 insert = 0; + if (pri->out >= pri->out_max) + { + if (fabs(error) > pri->err_limit && pri->detach) + { + insert = 0; + } + else + { + insert = 1; + if (error < 0) + { + temp_iterm = (pri->ki + self->pid_params.ki) * error; + } + } + } + else if (pri->out <= pri->out_min) + { + if (fabs(error) > pri->err_limit && pri->detach) + { + insert = 0; + } + else + { + insert = 1; + if (error > 0) + { + temp_iterm = (pri->ki + self->pid_params.ki) * error; + } + } + } + else + { + if (fabs(error) > pri->err_limit && pri->detach) + { + insert = 0; + } + else + { + insert = 1; + temp_iterm = (pri->ki + self->pid_params.ki) * error; + } + } + + pri->iout += temp_iterm; + + /* limt integral */ + if (pri->iout > pri->out_max) + { + pri->iout = pri->out_max; + } + else if (pri->iout < pri->out_min) + { + pri->iout = pri->out_min; + } + pri->iout = pri->iout * insert; + } + +#if INCOMPLETE_DIFFEREN == 1 + /*不完全微分*/ + thisdev = kd * (1.0 - pri->alpha) * (error - pri->e_1) + pri->alpha * pri->lastdev; + /*record last dev result*/ + pri->lastdev = thisdev; +#else + thisdev = (error - pri->e_1) * (kd); +#endif + + if (pri->kd_enable == FALSE) + { + thisdev = 0; + } + + if (pri->ki_enable == FALSE) + { + pri->iout = 0; + } + + pri->out = (pri->kp + self->pid_params.kp) * error + pri->iout + thisdev; + pri->e_1 = error; + /*record last feedback sensor result*/ + pri->pre_feedback = pri->feedback; + /*limt pid output*/ + pri->out = RANGE(pri->out, pri->out_min, pri->out_max); + return pri->out; +} + +static float32 increment_pid(struct PID_FUZZY *self, float32 target, float32 feedback) +{ + float32 ep, ei, ed; + float32 inc_out; + float32 thisdev = 0; + pid_common_increment_t *pri = &self->pri_u.increment; + + pri->feedback = feedback; + pri->e_0 = pri->ref - pri->feedback; + + if (pri->e_0 >= MAXE) + { + return pri->out_max; + } + else if (pri->e_0 <= MINE) + { + return pri->out_min; + } + + if (fabs(pri->e_0) <= pri->err_dead) + { + pri->e_0 = 0; + } + + ep = pri->e_0 - pri->e_1; + ei = pri->e_0; + ed = pri->e_0 - 2 * pri->e_1 + pri->e_2; + if (self->open == TRUE) + { + compensate(pri->e_0, ep, &self->pid_params); + } + + if (pri->sm == 1) + { + smooth_setpoint(self, target); + } + else + { + pri->ref = target; + } + +#if INCOMPLETE_DIFFEREN == 1 + /*不完全微分*/ + thisdev = (1.0 - pri->alpha) * (pri->kd + self->pid_params.kd) * ed + pri->alpha * pri->lastdev; +#else + ed = ed; +#endif + + if (self->speed_integral_enable == TRUE) + { + + if (ABS(pri->e_0) > MAXE) + { + pri->iout = (pri->ki + self->pid_params.ki) * ei; + } + else + { + // 变速积分 + pri->iout = (pri->ki + self->pid_params.ki) * ei * changing_integral_rate(self); + } + } + else + { + pri->iout = (pri->ki + self->pid_params.ki) * ei; + } + + if (pri->kd_enable == FALSE) + { + thisdev = 0; + } + + if (pri->ki_enable == FALSE) + { + pri->iout = 0; + } + + inc_out = (pri->kp + self->pid_params.kp) * ep + pri->iout + thisdev; + pri->e_2 = pri->e_1; + pri->e_1 = pri->e_0; + pri->lastdev = thisdev; + pri->out = pri->out + inc_out; + pri->out = RANGE(pri->out, pri->out_min, pri->out_max); + return pri->out; +} + +static float32 _pid(struct PID_FUZZY *self, float32 target, float32 feedback) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + return position_pid(self, target, feedback); + } + else + { + return increment_pid(self, target, feedback); + } +} + +/** + * @brief 复位PID积分及微分控制数据 + * @param {PID_FUZZY} *self + * @return {*} + */ +static void _restctrl(struct PID_FUZZY *self, float32 out) +{ + if (self->sub_type == PID_SUB_TYPE_POSITION) + { + pid_common_position_t *pri = NULL; + pri = &self->pri_u.position; + pri->e_1 = 0; + pri->iout = 0; + pri->out = out; + pri->iout = out; +#if INCOMPLETE_DIFFEREN == 1 + pri->lastdev = 0; +#endif + } + else + { + pid_common_increment_t *pri = NULL; + pri = &self->pri_u.increment; + pri->e_0 = 0; + pri->e_1 = 0; + pri->e_2 = 0; + pri->lastdev = 0; + pri->out = out; + pri->iout = out; + } +} + +void pid_fuzzy_constructor(struct PID_FUZZY *self) +{ + self->set_ctrl_prm = _set_ctrl_prm; + self->set_error_max_min = _set_error_max_min; + self->update_ctrl_prm = _update_ctrl_prm; + self->set_cfg = _set_cfg; + self->set_smooth_enable = _set_smooth_enable; + self->set_err_dead = _set_err_dead; + self->set_kp = _set_kp; + self->set_ki_enable = _set_ki_enable; + self->set_ki = _set_ki; + self->set_kd_enable = _set_kd_enable; + self->set_kd = _set_kd; + self->set_kd_dev = _set_kd_dev; + self->set_range = _set_range; + self->restctrl = _restctrl; + self->set_iout = _set_iout; + self->in_dead_zone = _in_dead_zone; + self->execute = _pid; +} diff --git a/lib/control/src/pid_neural.c b/lib/control/src/pid_neural.c new file mode 100644 index 0000000..93ffc03 --- /dev/null +++ b/lib/control/src/pid_neural.c @@ -0,0 +1,97 @@ +#include "pid.h" +#include +// 设置控制参数 +static void _set_ctrl_prm(struct PID_NEURAL *self, float32 minimum, float32 maximum) +{ + self->pri.setpoint = minimum; /*设定值*/ + + self->pri.kcoef = 0.12; /*神经元输出比例*/ + self->pri.kp = 0.45; /*比例学习速度*/ + self->pri.ki = 0.05; /*积分学习速度*/ + self->pri.kd = 0; /*微分学习速度*/ + + self->pri.lasterror = 0.0; /*前一拍偏差*/ + self->pri.preerror = 0.0; /*前两拍偏差*/ + self->pri.result = minimum; /*PID控制器结果*/ + self->pri.output = 0.0; /*输出值,百分比*/ + + self->pri.maximum = maximum; /*输出值上限*/ + self->pri.minimum = minimum; /*输出值下限*/ + self->pri.deadband = (maximum - minimum) * 0.0005f; /*死区*/ + + self->pri.wp = 0.10; /*比例加权系数*/ + self->pri.wi = 0.10; /*积分加权系数*/ + self->pri.wd = 0.10; /*微分加权系数*/ +} + +// 设置输出参数 +static void _set_out_prm(struct PID_NEURAL *self, float32 minimum, float32 maximum) +{ + self->pri.maximum = maximum; + self->pri.minimum = minimum; +} + +/*单神经元学习规则函数*/ +static void NeureLearningRules(struct PID_NEURAL *self, float32 zk, float32 uk, float32 *xi) +{ + self->pri.wi = self->pri.wi + self->pri.ki * zk * uk * xi[0]; + self->pri.wp = self->pri.wp + self->pri.kp * zk * uk * xi[1]; + self->pri.wd = self->pri.wd + self->pri.kd * zk * uk * xi[2]; +} + +static float32 _PID(struct PID_NEURAL *self, float32 target, float32 feedback) +{ + float32 x[3]; + float32 w[3]; + float32 sabs; + float32 error; + float32 result; + float32 deltaResult; + self->pri.setpoint = target; + error = self->pri.setpoint - feedback; + result = self->pri.result; + if (fabs(error) > self->pri.deadband) + { + x[0] = error; + x[1] = error - self->pri.lasterror; + x[2] = error - self->pri.lasterror * 2 + self->pri.preerror; + + sabs = fabs(self->pri.wi) + fabs(self->pri.wp) + fabs(self->pri.wd); + w[0] = self->pri.wi / sabs; + w[1] = self->pri.wp / sabs; + w[2] = self->pri.wd / sabs; + + deltaResult = (w[0] * x[0] + w[1] * x[1] + w[2] * x[2]) * self->pri.kcoef; + } + else + { + deltaResult = 0; + } + + result = result + deltaResult; + if (result > self->pri.maximum) + { + result = self->pri.maximum; + } + if (result < self->pri.minimum) + { + result = self->pri.minimum; + } + self->pri.result = result; + self->pri.output = self->pri.result; + + // 单神经元学习 + NeureLearningRules(self, error, result, x); + + self->pri.preerror = self->pri.lasterror; + self->pri.lasterror = error; + + return self->pri.output; +} + +void pid_neural_constructor(struct PID_NEURAL *self) +{ + self->set_ctrl_prm = _set_ctrl_prm; + self->set_out_prm = _set_out_prm; + self->PID = _PID; +} diff --git a/lib/control/src/s_curve.c b/lib/control/src/s_curve.c new file mode 100644 index 0000000..9d40b6c --- /dev/null +++ b/lib/control/src/s_curve.c @@ -0,0 +1,171 @@ +#include "s_curve.h" + +// s曲线加速度各段参数定义 +// 起始速度 +#define F0 0.0f + +// 加加速度与减减速度 +#define FAA 0.0f +#define FRR 0.0f + +// 加速段三个时间 +#define TAA 0.1f +#define TUA 0.2f +#define TRA 0.1f + +// 匀速段 +#define TUU 5.0f + +// 减速段 +#define TAR 0.1f +#define TUR 0.2f +#define TRR 0.1f + +// 表格长度 +#define S_CURVE_TABLE_LEN 400 +int16_t s_curve_table[S_CURVE_TABLE_LEN] = {0}; + +static int16_t s_curve_func(s_curve_t *s, float32 t, float32 *freq, float32 *acc) +{ + // 辅助常数项 + float32 A, B, C, D, E, F; + // 表达式中间值 + float32 f3, f4, f5; + // 加速段,匀速段,减速段时间 + float32 Ta, Tu, Tr; + // 减加速与加减速段斜率 + float32 fra, far; + // 起始频率与加加速频率,减减速频率 + float32 f0, faa, frr; + float32 taa, tua, tra, tuu, tar, tur, trr; + + // 获取参数 + faa = s->faa; + frr = s->frr; + + taa = s->taa; + tua = s->tua; + tra = s->tra; + tuu = s->tuu; + tar = s->tar; + tur = s->tur; + trr = s->trr; + + f0 = s->f0; + + fra = faa * taa / tra; + far = frr * trr / tar; + + Ta = taa + tua + tra; + Tu = tuu; + Tr = tar + tur + trr; + + A = f0; + B = f0 - 0.5 * faa * taa * taa; + C = f0 + 0.5 * faa * taa * taa + faa * taa * tua + 0.5 * fra * (taa + tua) * (taa + tua) - fra * Ta * (taa + tua); + + // f1 = f0 + 0.5 * faa * taa * taa; + // f2 = f0 + 0.5 * faa * taa * taa + faa * taa * tua; + f3 = 0.5 * fra * Ta * Ta + C; + + D = f3 - 0.5 * far * (Ta + Tu) * (Ta + Tu); + f4 = -far * 0.5 * (Ta + Tu + tar) * (Ta + Tu + tar) + far * (Ta + Tu) * (Ta + Tu + tar) + D; + + E = f4 + far * tar * (Ta + Tu + tar); + f5 = -far * tar * (Ta + Tu + Tr - trr) + E; + + F = f5 + frr * (Ta + Tu + Tr) * (Ta + Tu + Tr - trr) - 0.5 * frr * (Ta + Tu + Tr - trr) * (Ta + Tu + Tr - trr); + + // 如果时间点在全行程规定的时间段内 + if ((t >= 0) && (t <= Ta + Tu + Tr)) + { + // 加加速段 + if ((t >= 0) && (t <= taa)) + { + *freq = 0.5 * faa * t * t + A; + *acc = faa * t; + } + // 匀加速段 + else if ((t >= taa) && (t <= taa + tua)) + { + *freq = faa * taa * t + B; + *acc = faa * taa; + } + // 加减速段 + else if ((t >= taa + tua) && (t <= taa + tua + tra)) + { + *freq = -0.5 * fra * t * t + fra * Ta * t + C; + *acc = -fra * t + fra * Ta; + } + // 匀速段 + else if ((t >= Ta) && (t <= Ta + tuu)) + { + *freq = f3; + *acc = 0; + } + // 加减速段 + else if ((t >= Ta + Tu) && (t <= Ta + Tu + tar)) + { + *freq = -0.5 * far * t * t + far * (Ta + Tu) * t + D; + *acc = -far * t + far * (Ta + Tu); + } + // 匀减速 + else if ((t >= Ta + Tu + tar) && (t <= Ta + Tu + tar + tur)) + { + *freq = -far * tar * t + E; + *acc = -far * tar; + } + // 减减速 + else if ((t >= Ta + Tu + Tr - trr) && (t <= Ta + Tu + Tr)) + { + *freq = 0.5 * frr * t * t - frr * (Ta + Tu + Tr) * t + F; + *acc = frr * t - frr * (Ta + Tu + Tr); + } + } + else + { + return -1; + } + + return 0; +} + +// S型曲线初始化 +void s_curve_table_gen(uint16_t tmin, uint16_t tmax) +{ + uint16_t i, tint; + float32 ti; + float32 freq; + float32 acc; + float32 fi; + s_curve_t s; + osel_memset((uint8_t *)&s, 0, sizeof(s_curve_t)); + + s.f0 = F0; + s.taa = TAA; + s.tua = TUA; + s.tra = TRA; + s.tuu = TUU; + s.tar = TAR; + s.tur = TUR; + s.trr = TRR; + + // 根据约束条件求出加加速段与减减速段斜率 + s.faa = 2.0 / (s.taa * (s.taa + s.tra + 2 * s.tua)); + s.frr = 2.0 / (s.trr * (s.tar + s.trr + 2 * s.tur)); + + for (i = 0; i < S_CURVE_TABLE_LEN; i++) + { + // 求出每个时间点对应的频率以及加速度 + fi = i * (TAA + TUA + TRA + TUU + TAR + TUR + TRR) / S_CURVE_TABLE_LEN; + s_curve_func(&s, fi, &freq, &acc); + + // 根据最大与最小装载值确定定时器实际值 + ti = tmax - (tmax - tmin) * freq; + // 转换为整数值 + tint = (uint16_t)ti; + + // 存入s曲线表 + s_curve_table[i] = tint; + } +} diff --git a/lib/control/模糊PID控制器设计文档.md b/lib/control/模糊PID控制器设计文档.md new file mode 100644 index 0000000..c9cf8b3 --- /dev/null +++ b/lib/control/模糊PID控制器设计文档.md @@ -0,0 +1,111 @@ +# 模糊PID控制器设计文档 + +# 模糊PID控制器详细设计文档 + +## 1. 引言 + +### 1.1 目的 + +本文档旨在详细介绍模糊PID控制器的设计理念、实现方法和使用指南,为开发者提供一套完整的模糊PID控制解决方案。 + +### 1.2 背景 + +PID控制器因其结构简单、稳定性好、易于实现等优点,在工业控制系统中得到了广泛应用。然而,传统PID控制器在面对复杂或非线性系统时,性能表现不佳。模糊PID控制器通过引入模糊逻辑,动态调整PID参数,以适应系统在不同工作状态下的控制需求,从而提高控制性能。 + +## 2. 设计概述 + +### 2.1 设计目标 + +- **适应性**:能够适应不同类型和不同工作状态的控制系统。 +- **稳定性**:保证控制系统在各种工作条件下的稳定运行。 +- **易用性**:提供简单易懂的接口,便于开发者快速实现和调试。 + +### 2.2 功能模块 + +模糊PID控制器主要包括以下几个功能模块: + +1. **模糊控制模块**:负责根据输入的误差和误差变化率,通过模糊逻辑计算出PID参数。 +2. **SV平滑给定模块**:负责平滑控制目标值,减少控制过程中的突变。 +3. **变速积分模块**:根据误差的大小调整积分速率,提高控制效率。 +4. **参数设置模块**:提供接口函数,用于设置和调整PID参数。 + +## 3. 功能模块详细设计 + +### 3.1 模糊控制模块 + +#### 3.1.1 输入处理 + +- **误差处理**:将实时误差 `e`限制在预定的范围内,并进行模糊化处理。 +- **误差变化率处理**:将误差变化率 `ec`进行相同的处理。 + +#### 3.1.2 模糊规则库 + +- **规则定义**:根据系统的具体需求,定义一套模糊规则,用于计算PID参数。 +- **规则应用**:根据输入的误差和误差变化率的模糊化值,通过模糊规则库计算出 `kp`、`ki`、`kd`。 + +### 3.2 SV平滑给定模块 + +- **平滑策略**:根据当前目标值与新目标值之间的差值,动态调整目标值变化的步长,实现平滑过渡。 + +### 3.3 变速积分模块 + +- **积分策略**:根据误差的大小,调整积分速率。误差较小时,使用完整积分;误差较大时,减小或停止积分。 + +### 3.4 参数设置模块 + +- **接口设计**:提供一系列接口函数,用于设置PID控制器的参数,如输出限制、死区误差等。 + +## 4. 使用说明 + +### 4.1 初始化 + +- **控制器初始化**:根据控制对象的特性,初始化模糊PID控制器的相关参数和模糊规则库。 + +### 4.2 实时控制 + +- **参数调整**:在控制循环中,根据实时误差和误差变化率,动态调整PID参数。 +- **控制执行**:根据调整后的PID参数,执行PID控制算法,输出控制信号。 + +### 4.3 参数调整 + +- **动态调整**:根据系统运行情况,通过参数设置模块调整PID参数,优化控制效果。 + +## 5. 结论 + +模糊PID控制器通过动态调整PID参数,提高了控制系统的适应性和稳定性,特别适用于复杂或非线性系统的控制。本文档提供了模糊PID控制器的详细设计方案,旨在帮助开发者更好地理解和应用模糊PID控制技术 + +## 概述 + +本文档旨在详细介绍模糊PID控制器的设计与实现。模糊PID控制器结合了传统PID控制和模糊逻辑控制的优点,通过模糊逻辑对PID参数进行动态调整,以适应控制系统在不同工作状态下的需求。 + +## 功能模块 + +### 1. 模糊控制模块 + +- **功能描述**:根据误差 `e`和误差变化率 `ec`的模糊化值,通过模糊规则库计算出模糊PID控制器的三个参数:比例系数 `kp`、积分系数 `ki`、微分系数 `kd`。 +- **实现方法**:首先将输入的误差 `e`和误差变化率 `ec`限制在预定范围内,然后通过模糊化处理得到其隶属度和模糊位置标号,最后根据模糊规则库计算出 `kp`、`ki`、`kd`的值。 + +### 2. SV平滑给定模块 + +- **功能描述**:平滑控制目标值(Setpoint Value, SV),以减少控制过程中的突变,提高系统的稳定性。 +- **实现方法**:根据当前目标值与新目标值之间的差值,动态调整目标值的变化步长,以实现平滑过渡。 + +### 3. 变速积分模块 + +- **功能描述**:根据误差的大小调整积分速率,以提高控制系统的快速性和稳定性。 +- **实现方法**:当误差较小时,使用完整积分;当误差在一定范围内变化时,通过线性函数调整积分速率;当误差较大时,减小或停止积分,以避免积分饱和。 + +### 4. 参数设置模块 + +- **功能描述**:提供接口函数,用于设置PID控制器的各项参数,包括输出限制范围、死区误差、积分输出值、PID参数等。 +- **实现方法**:根据控制器的子类型(位置型或增量型),分别设置相应参数的值。 + +## 使用说明 + +1. **初始化**:根据控制对象的具体情况,初始化模糊PID控制器的结构体,包括最大误差、最小误差、PID参数的模糊规则库等。 +2. **实时控制**:在控制循环中,根据当前的误差 `e`和误差变化率 `ec`调用模糊控制模块,计算出动态调整的PID参数,然后根据这些参数进行PID控制。 +3. **参数调整**:根据系统运行情况,通过参数设置模块调整PID控制器的参数,以优化控制效果。 + +## 结论 + +模糊PID控制器通过引入模糊逻辑,使得PID参数能够根据控制系统的实时状态动态调整,从而提高了控制系统的适应性和稳定性。通过本文档的设计与实现,开发者可以更好地理解和应用模糊PID控制器。 diff --git a/lib/control/自整定.md b/lib/control/自整定.md new file mode 100644 index 0000000..74ed869 --- /dev/null +++ b/lib/control/自整定.md @@ -0,0 +1 @@ +https://www.cnblogs.com/foxclever/p/16299063.html diff --git a/lib/driver/dac161p997.c b/lib/driver/dac161p997.c new file mode 100644 index 0000000..015b829 --- /dev/null +++ b/lib/driver/dac161p997.c @@ -0,0 +1,161 @@ +#include "dac161p997.h" +#include "delay.h" + +static dac161p997_t _handle; + +static void dac161p997_output_0() +{ + _handle.io->set(*_handle.io); + delay_us(DUTY_CYCLE_25); + _handle.io->reset(*_handle.io); + delay_us(DUTY_CYCLE_75); +} + +static void dac161p997_output_1() +{ + _handle.io->set(*_handle.io); + delay_us(DUTY_CYCLE_75); + _handle.io->reset(*_handle.io); + delay_us(DUTY_CYCLE_25); +} + +static void dac161p997_output_d() +{ + _handle.io->set(*_handle.io); + delay_us(DUTY_CYCLE_50); + _handle.io->reset(*_handle.io); + delay_us(DUTY_CYCLE_50); +} + +/** + * @brief 输出DAC161符号 + * + * 根据传入的符号类型,调用相应的DAC161输出函数。 + * + * @param sym 符号类型,可以是ZERO_SYM、ONE_SYM或其他值。 + */ +static void dac161p997_output_symbol(uint8_t sym) +{ + switch (sym) + { + case ZERO_SYM: + dac161p997_output_0(); + break; + case ONE_SYM: + dac161p997_output_1(); + break; + default: + dac161p997_output_d(); + break; + } +} + +/** + * @brief 向DAC161写入寄存器 + * + * 向DAC161写入一个16位的数据,并附加一个8位的标签。 + * 芯片会回复一个应答信号,此处忽略应答信号。可以通过设置寄存器关闭应答信号。 + * @param data 要写入的数据,16位。 + * @param tag 附加的标签,8位。tag = 0时,写DAC寄存器;tag = 1时,写配置寄存器。 + */ +void dac161p997_swif_write_reg(uint16_t data, uint8_t tag) +{ + uint8_t plow, phigh; + uint16_t i, tmp; + + /* compute parity low */ + tmp = data & 0x00ff; // get least significant byte + for (plow = 0; tmp != 0; tmp >>= 1) + { + if (tmp & 0x1) // test if lsb is 1 + plow++; // count number of bits equal to 1 + } + + if (plow & 0x1) // check if number of 1s is odd + plow = 0; // set even parity + else + plow = 1; // else set odd parity + + /* compute parity high */ + tmp = (data & 0xff00) >> 8; // get most significant byte + for (phigh = 0; tmp != 0; tmp >>= 1) + { + if (tmp & 0x1) // test if lsb is 1 + phigh++; // count number of bits equal to 1 + } + + if (phigh & 0x1) // check if number of 1s is odd + phigh = 0; // set even parity + else + phigh = 1; // set odd parity + + phigh = phigh ^ tag; // parity high is for high slice = tag + 1 byte + + dac161p997_output_symbol(IDLE_SYM); // Frame start: send an idle symbol first + dac161p997_output_symbol(tag); + + tmp = data; + for (i = 0; i < 16; i++) // send 16 data bits msb to lsb + { + if (tmp & 0x8000) + { + dac161p997_output_symbol(ONE_SYM); // send 1 + } + else + { + dac161p997_output_symbol(ZERO_SYM); // send 0 + } + + tmp = tmp << 1; // move next data bit to msb + } + + dac161p997_output_symbol(phigh); // send parity high bit + dac161p997_output_symbol(plow); // send parity low bit + + dac161p997_output_symbol(IDLE_SYM); // send idle +} + +/** + * @brief 设置DAC161输出电流 + * + * 通过向DAC161写入指定的电流值来设置其输出电流,输出电流持续时间100ms。 + * + * @param current 要设置的电流值 + */ +void dac161p997_output_current(float32 current) +{ + uint8_t adc = (uint16_t)(current * DAC161P997_CURRENT_SLOPE) >> 8; + static uint8_t last_adc; + if (adc == last_adc) + { + last_adc = adc; + return; + } + + dac161p997_swif_write_reg(DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_ERR_LOW_REG + adc, CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_LCK_REG_LOCK, CONFIG_WRITE); +} + +/** + * @brief 初始化DAC161设备 + * + * 此函数用于初始化DAC161设备,进行必要的配置,以便设备正常工作。 + * 写寄存器之前要先解锁,具体配置寄存器根据需要配置的寄存器而定 + * 具体步骤包括: + * 1. 解锁DAC161的配置寄存器 + * 2. 配置DAC161的错误下限寄存器 + * 3. 锁定DAC161的配置寄存器 + */ +void dac161p997_init() +{ + _handle.io = gpio_create(DAC161P997_IO_PORT, DAC161P997_IO_PIN); + dac161p997_swif_write_reg(DAC161P997_LCK_REG_UNLOCK, CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_CONFIG2_REG + + DAC161P997_CONFIG2_REG_LOOP + + DAC161P997_CONFIG2_REG_CHANNEL + + DAC161P997_CONFIG2_REG_PARITY + + DAC161P997_CONFIG2_REG_FRAME, + CONFIG_WRITE); + dac161p997_swif_write_reg(DAC161P997_LCK_REG_LOCK, CONFIG_WRITE); +} diff --git a/lib/driver/dac161p997.h b/lib/driver/dac161p997.h new file mode 100644 index 0000000..5c53d0b --- /dev/null +++ b/lib/driver/dac161p997.h @@ -0,0 +1,81 @@ +#ifndef __DAC161P997_H__ +#define __DAC161P997_H__ +#include "main.h" +#include "gpios.h" + +#define DAC161P997_IO_PORT (DAC161P997_GPIO_Port) +#define DAC161P997_IO_PIN (DAC161P997_Pin) + +#define DAC161P997_CURRENT_SLOPE 2730.625f // adc = (current / 24) * 0xffff + +// Symbol Periods +#define DUTY_CYCLE_100 (1000U) // (CPU_CLK / BAUD_RATE) +#define DUTY_CYCLE_75 (DUTY_CYCLE_100 * 0.75f) +#define DUTY_CYCLE_50 (DUTY_CYCLE_100 * 0.50f) +#define DUTY_CYCLE_25 (DUTY_CYCLE_100 * 0.25f) + +/************************************************************ + * TI DAC161P997 REGISTER SET ADDRESSES + ************************************************************/ +#define DAC161P997_LCK_REG (0x0000) +#define DAC161P997_CONFIG1_REG (0x0100) +#define DAC161P997_CONFIG2_REG (0x0200) +#define DAC161P997_CONFIG3_REG (0x0300) +#define DAC161P997_ERR_LOW_REG (0x0400) +#define DAC161P997_ERR_HIGH_REG (0x0500) + +// TI DAC161P997 Register Bits +#define DAC161P997_LCK_REG_LOCK (0x00AA) // any value other than 0x95 +#define DAC161P997_LCK_REG_UNLOCK (0x0095) +#define DAC161P997_CONFIG1_REG_RST (0x0001) +#define DAC161P997_CONFIG1_REG_NOP (0 * 0x08u) +#define DAC161P997_CONFIG1_REG_SET_ERR (1 * 0x08u) +#define DAC161P997_CONFIG1_REG_CLEAR_ERR (2 * 0x08u) +#define DAC161P997_CONFIG1_REG_NOP3 (3 * 0x08u) +#define DAC161P997_CONFIG2_REG_LOOP (0x0001) +#define DAC161P997_CONFIG2_REG_CHANNEL (0x0002) +#define DAC161P997_CONFIG2_REG_PARITY (0x0004) +#define DAC161P997_CONFIG2_REG_FRAME (0x0008) +#define DAC161P997_CONFIG2_REG_ACK_EN (0x0010) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_16 (0x000F) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_8 (0x0007) +#define DAC161P997_CONFIG3_REG_RX_ERR_CNT_1 (0x0000) + +// Tags +#define DACCODE_WRITE (0x00) +#define CONFIG_WRITE (0x01) + +// Valid Symbols +#define ZERO_SYM (0x00) +#define ONE_SYM (0x01) +#define IDLE_SYM (0x02) +#define STATIC_LOW_SYM (0x03) + +// DAC Codes for different currents +#define DACCODE_0mA (0x0000) +#define DACCODE_4mA (0x2AAA) +#define DACCODE_8mA (0x5555) +#define DACCODE_12mA (0x7FFF) +#define DACCODE_16mA (0xAAAA) +#define DACCODE_20mA (0xD555) +#define DACCODE_24mA (0xFFFF) + +// cycles +// #define QUARTER_CYCLES (625) +// #define HALF_CYCLES (1250) +// #define THREE_CYCLES (1875) +// #define FULL_CYCLES (2500) + +typedef struct +{ + gpio_t *io; +} dac161p997_t; + +void dac161p997_output_0(void); +void dac161p997_output_1(void); +void dac161p997_output_d(void); +void dac161p997_output_symbol(uint8_t sym); +void dac161p997_swif_write_reg(uint16_t data, uint8_t tag); +extern void dac161p997_output_current(float32 current); +extern void dac161p997_init(void); +#endif diff --git a/lib/driver/eeprom_fm24.c b/lib/driver/eeprom_fm24.c new file mode 100644 index 0000000..2fd37e9 --- /dev/null +++ b/lib/driver/eeprom_fm24.c @@ -0,0 +1,219 @@ +/** + * @file eeprom_fm24.c + * @author xxx + * @date 2023-08-29 07:58:27 + * @brief 用于实现FM24 EEPROM相关的读写操作 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#include "entity.h" +#include "board.h" +#include "eeprom_fm24.h" +#include "delay.h" + +#define FM24_SPI SPI1 + +#define EEPROM_FM24_CS_PORT EE3_CS_GPIO_Port +#define EEPROM_FM24_CS_PIN EE3_CS_Pin +#define EEPROM_FM24_MOSI_PORT SPI_MOSI_GPIO_Port +#define EEPROM_FM24_MOSI_PIN SPI_MOSI_Pin +#define EEPROM_FM24_MISO_PORT SPI_MISO_GPIO_Port +#define EEPROM_FM24_MISO_PIN SPI_MISO_Pin +#define EEPROM_FM24_SCK_PORT SPI_CLK_GPIO_Port +#define EEPROM_FM24_SCK_PIN SPI_CLK_Pin + +static fm24_t _eeprom_fm24; + +void eeprom_fm24_init(void) +{ + spi_gpio_group_t gpios; + spi_normal_config_t cfg; + osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t)); + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_FM24_CS_PORT, EEPROM_FM24_CS_PIN); + gpios.mosi = gpio_create(EEPROM_FM24_MOSI_PORT, EEPROM_FM24_MOSI_PIN); + gpios.sck = gpio_create(EEPROM_FM24_SCK_PORT, EEPROM_FM24_SCK_PIN); + gpios.miso = gpio_create(EEPROM_FM24_MISO_PORT, EEPROM_FM24_MISO_PIN); + gpios.rst = gpio_create(NULL, 0); + gpios.rdy = gpio_create(NULL, 0); + + // 创建SPI对象 + eeprom_fm24_get()->spi = spi_create(SPI_TYPE_NORMAL, gpios, 0); + DBG_ASSERT(eeprom_fm24_get() != NULL __DBG_LINE); + cfg.cmd_rdsr = FM24_CMD_RDSR; + cfg.cmd_wrsr = FM24_CMD_WRSR; + cfg.cmd_wren = FM24_CMD_WREN; + cfg.cmd_wrdi = FM24_CMD_WRDI; + cfg.cmd_write = FM24_CMD_WRITE; + cfg.cmd_read = FM24_CMD_READ; + cfg.dummy_byte = FM24_DUMMY_BYTE; + cfg.address_bytes = 2; + cfg.page_size = FM24_PAGE_SIZE; + cfg.total_size = FM24_SIZE; + cfg.continuous_write = FALSE; + osel_memcpy((uint8_t *)&eeprom_fm24_get()->spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t)); + // 使能SPI + eeprom_fm24_get()->spi->interface.hardware_enable(eeprom_fm24_get()->spi, FM24_SPI); + // 这里需要复位下SPI,否则读出的数据不对 + eeprom_fm24_get()->spi->interface.u.normal.spi_reset(eeprom_fm24_get()->spi); + + eeprom_fm24_write_protection_close(); + // eeprom_fm24_test(); +} + +fm24_t *eeprom_fm24_get(void) +{ + return &_eeprom_fm24; +} + +void eeprom_fm24_dinit(void) +{ + LL_SPI_Disable(FM24_SPI); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.mosi->port, eeprom_fm24_get()->spi->gpios.mosi->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.miso->port, eeprom_fm24_get()->spi->gpios.miso->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.sck->port, eeprom_fm24_get()->spi->gpios.sck->pin); + GPIO_SET_ANALOG(eeprom_fm24_get()->spi->gpios.cs->port, eeprom_fm24_get()->spi->gpios.cs->pin); +} + +void eeprom_fm24_enable(void) +{ + uint16_t count = 100; + LL_SPI_Enable(FM24_SPI); + // 判断SPI是否使能成功 + while (LL_SPI_IsEnabled(FM24_SPI) != 1) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +void eeprom_fm24_disable(void) +{ + uint16_t count = 100; + LL_SPI_Disable(FM24_SPI); + // 判断SPI是否关闭成功 + while (LL_SPI_IsEnabled(FM24_SPI) != 0) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief 关闭EEPROM FM24的写保护 + * + * 此函数用于关闭EEPROM FM24的写保护功能,允许对其进行写操作。 + * + * 调用此函数前,应确保EEPROM FM24的SPI接口已正确初始化。 + */ +void eeprom_fm24_write_protection_close(void) +{ + DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE); + eeprom_fm24_enable(); + eeprom_fm24_get()->spi->interface.u.normal.spi_write_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_wrsr, 0); + eeprom_fm24_disable(); +} + +/** + * @brief 获取EEPROM FM24的写保护状态 + * + * 获取EEPROM FM24的写保护状态。 + * + * @return BOOL 返回TRUE表示没有写保护,返回FALSE表示有写保护 + */ +BOOL eeprom_fm24_write_protection_state(void) +{ + DBG_ASSERT(eeprom_fm24_get()->spi != NULL __DBG_LINE); + eeprom_fm24_enable(); + eeprom_fm24_get()->write_protection.data = eeprom_fm24_get()->spi->interface.u.normal.spi_read_reg(eeprom_fm24_get()->spi, eeprom_fm24_get()->spi->cfg.cmd_rdsr); + eeprom_fm24_disable(); + + if (eeprom_fm24_get()->write_protection.bits.bp0 == 1 || + eeprom_fm24_get()->write_protection.bits.bp1 == 1 || + eeprom_fm24_get()->write_protection.bits.wpen == 1) + { + return FALSE; + } + else + { + return TRUE; + } +} + +BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + // 开启和关闭SPI对读写实时性有影响 + eeprom_fm24_enable(); + ret = eeprom_fm24_get()->spi->interface.u.normal.spi_write(eeprom_fm24_get()->spi, write_addr, data, length); + eeprom_fm24_disable(); + return ret; +} + +BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + // 开启和关闭SPI对读写实时性有影响 + eeprom_fm24_enable(); + ret = eeprom_fm24_get()->spi->interface.u.normal.spi_read(eeprom_fm24_get()->spi, read_addr, data, length); + eeprom_fm24_disable(); + return ret; +} + +BOOL eeprom_fm24_test(void) +{ + const uint8_t buf_size = 5; + uint16_t test_address = FM24_TEST_PAGE * FM24_PAGE_SIZE; + uint8_t buf[buf_size]; + uint8_t rbuf[buf_size]; + osel_memset(buf, 0, buf_size); + osel_memset(rbuf, 0, buf_size); + buf[0] = 0xD5; + buf[1] = 0xC8; + buf[2] = 0x00; + buf[3] = 0x01; + buf[4] = 0x02; + buf[buf_size - 1] = 0xfe; + eeprom_fm24_write(test_address, buf, buf_size); + __NOP(); + eeprom_fm24_read(test_address, rbuf, buf_size); + if (osel_memcmp(buf, rbuf, buf_size) == 0) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 获取FM24 EEPROM的状态 + * + * 获取FM24 EEPROM的当前状态。该函数始终返回TRUE,表示EEPROM正常工作。 + * + * @return BOOL 始终返回TRUE + */ +BOOL eeprom_fm24_status_get(void) +{ + return TRUE; +} diff --git a/lib/driver/eeprom_fm24.h b/lib/driver/eeprom_fm24.h new file mode 100644 index 0000000..dd7f702 --- /dev/null +++ b/lib/driver/eeprom_fm24.h @@ -0,0 +1,158 @@ +/** + * @file eeprom_fm24.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief FM24系列EEPROM驱动 https://zhuanlan.zhihu.com/p/598934638 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __EEPROM_FM24_H__ +#define __EEPROM_FM24_H__ +#include "main.h" +#include "spis.h" +// High-endurance 100 trillion (1014) read/writes + +//========在此设定芯片地址============= + +#define W_ADD_COM 0xa0 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0 + +#define R_ADD_COM 0xa1 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1 + +//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512 + +//=======在此设定芯片型号, 1代表24C01; 16代表24C16; 512代表24C512 + +#define e2prom 256 // + +#if e2prom == 1 +#define FM24_PAGE_SIZE 16 +#define FM24_SIZE (128 * 8) +#elif e2prom == 2 +#define FM24_PAGE_SIZE 16 +#define FM24_SIZE (256 * 8) +#elif e2prom == 4 +#define FM24_PAGE_SIZE 32 +#define FM24_SIZE (512 * 8) +#elif e2prom == 8 +#define FM24_PAGE_SIZE 64 +#define FM24_SIZE (1024 * 8) +#elif e2prom == 16 +#define FM24_PAGE_SIZE 128 +#define FM24_SIZE (2048 * 8) +#elif e2prom == 32 +#define FM24_PAGE_SIZE 128 +#define FM24_SIZE (4096 * 8) +#elif e2prom == 64 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (8192 * 8) +#elif e2prom == 128 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (16384) +#elif e2prom == 256 +#define FM24_PAGE_SIZE 256 +#define FM24_SIZE (32768) // 32K 128页 +#elif e2prom == 512 +#define FM24_PAGE_SIZE 512 +#define FM24_SIZE (65536) +#endif + +#define FM24_CMD_RDSR 0x05 /*!< Read Status Register instruction */ +#define FM24_CMD_WRSR 0x01 /*!< Write Status Register instruction */ + +#define FM24_CMD_WREN 0x06 /*!< Write enable instruction */ +#define FM24_CMD_WRDI 0x04 /*!< Write disable instruction */ + +#define FM24_CMD_READ 0x03 /*!< Read from Memory instruction */ +#define FM24_CMD_WRITE 0x02 /*!< Write to Memory instruction */ + +#define FM24_DUMMY_BYTE 0x00 ///< 无用数据 + +#define FM24_TEST_PAGE 0 ///< 测试页地址 + +typedef union +{ + uint8_t data; + struct + { + uint8_t reserve1 : 1; + uint8_t wel : 1; ///< Write enable latch + uint8_t bp0 : 1; ///< Block protect 0 + uint8_t bp1 : 1; ///< Block protect 1 + uint8_t reserve2 : 3; + uint8_t wpen : 1; ///< Write protect enable + } bits; +} fm24_write_protection_u; + +typedef struct +{ + fm24_write_protection_u write_protection; + spi_t *spi; +} fm24_t; + +/** + * @brief Initializes the FM24 EEPROM module. + */ +extern void eeprom_fm24_init(void); + +/** + * @brief Deinitializes the FM24 EEPROM module. + */ +extern void eeprom_fm24_dinit(void); + +/** + * @brief Gets the fm24_t handle of the FM24 EEPROM module. + * @return The fm24_t handle of the FM24 EEPROM module. + */ +extern fm24_t *eeprom_fm24_get(void); + +/** + * @brief Enables the FM24 EEPROM module. + */ +extern void eeprom_fm24_enable(void); + +/** + * @brief Disables the FM24 EEPROM module. + */ +extern void eeprom_fm24_disable(void); + +/** + * @brief Reads data from the FM24 EEPROM module. + * @param read_addr The starting address to read from. + * @param data Pointer to the buffer to store the read data. + * @param length The number of bytes to read. + * @return TRUE if the read operation is successful, FALSE otherwise. + */ +extern BOOL eeprom_fm24_read(uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Writes data to the FM24 EEPROM module. + * @param write_addr The starting address to write to. + * @param data Pointer to the data to be written. + * @param length The number of bytes to write. + * @return TRUE if the write operation is successful, FALSE otherwise. + */ +extern BOOL eeprom_fm24_write(uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Closes the write protection of the FM24 EEPROM module. + */ +extern void eeprom_fm24_write_protection_close(void); + +/** + * @brief Gets the write protection state of the FM24 EEPROM module. + * @return TRUE if the FM24 EEPROM module is not write-protected, FALSE otherwise. + */ +extern BOOL eeprom_fm24_write_protection_state(void); + +/** + * @brief Performs a test on the FM24 EEPROM module. + */ +extern BOOL eeprom_fm24_test(void); + +/** + * @brief Gets the status of the FM24 EEPROM module. + * @return TRUE if the FM24 EEPROM module is ready, FALSE otherwise. + */ +extern BOOL eeprom_fm24_status_get(void); + +#endif // __EEPROM_FM24_H__ diff --git a/lib/driver/eeprom_lc02b.c b/lib/driver/eeprom_lc02b.c new file mode 100644 index 0000000..6608ce4 --- /dev/null +++ b/lib/driver/eeprom_lc02b.c @@ -0,0 +1,152 @@ +#include "eeprom_lc02b.h" +#include "delay.h" + +#define W_ADD_COM 0xa8 // 写字节命令及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 0 +#define R_ADD_COM 0xa9 // 读命令字节及器件地址(根据地址实际情况改变), 1010 A2 A1 A0 1 +#define PAGE_SIZE 8U +#define SIZE 256U + +#define EEPROM_LC02B_SDA_PORT I2C1_SDA_GPIO_Port +#define EEPROM_LC02B_SDA_PIN I2C1_SDA_Pin +#define EEPROM_LC02B_SCL_PORT I2C1_SCL_GPIO_Port +#define EEPROM_LC02B_SCL_PIN I2C1_SCL_Pin + +static i2c_t *eeprom_24lc028bt_i2c; + +void eeprom_lc02b_test(void) +{ +#define TEST_SIZE 15 + uint16_t test_address = SIZE - TEST_SIZE; + uint8_t buf[TEST_SIZE]; + for (uint8_t i = 0; i < TEST_SIZE; i++) + { + buf[i] = i + 1; + } + + eeprom_lc02b_write(test_address, buf, TEST_SIZE); + LL_mDelay(10); + osel_memset(buf, 0, ARRAY_LEN(buf)); + eeprom_lc02b_read(test_address, buf, TEST_SIZE); + + __NOP(); +} + +/** + * @brief 获取EEPROM LC02B的状态 + * + * 此函数用于获取EEPROM LC02B的当前状态。 + * + * @return 如果EEPROM LC02B处于正常状态,则返回TRUE;否则返回FALSE。 + * 注意:在本实现中,总是返回TRUE,因为这是一个简单的示例函数。 + */ +BOOL eeprom_lc02b_status_get(void) +{ + return TRUE; +} + +/** + * @brief EEPROM LC02B初始化 + * @return {*} + * @note + */ +void eeprom_lc02b_init(void) +{ + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN); + gpios.sda = gpio_create(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN); + + eeprom_24lc028bt_i2c = i2c_create(gpios, 10); + // eeprom_lc02b_test(); +} + +/** + * @brief EEPROM LC02B反初始化 + * @return {*} + * @note + */ +void eeprom_lc02b_dinit(void) +{ + GPIO_SET_ANALOG(EEPROM_LC02B_SDA_PORT, EEPROM_LC02B_SDA_PIN); + GPIO_SET_ANALOG(EEPROM_LC02B_SCL_PORT, EEPROM_LC02B_SCL_PIN); +} +/** + * @brief 写入数据 + * @param {uint32_t} write_addr + * @param {uint8_t} *data + * @param {uint16_t} length + * @return {*} + * @note + */ +void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length) +{ + // 发送开始信号 + eeprom_24lc028bt_i2c->interface.start(eeprom_24lc028bt_i2c); + // 发送写入地址命令 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, W_ADD_COM); + // 等待写入地址命令响应 + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + // 发送要写入的地址 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, (uint8_t)write_addr); + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + + // 循环写入数据 + + for (uint16_t i = 0; i < length; i++) + { + // 写入一个字节数据 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, *data++); + // 等待响应 + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + write_addr++; + if (write_addr % PAGE_SIZE == 0) + { + eeprom_24lc028bt_i2c->interface.stop(eeprom_24lc028bt_i2c); + LL_mDelay(10); // 延时10ms,等待写入完成 + eeprom_24lc028bt_i2c->interface.start(eeprom_24lc028bt_i2c); + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, W_ADD_COM); + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, (uint8_t)write_addr); + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + } + } + // 写入完成,停止I2C总线 + eeprom_24lc028bt_i2c->interface.stop(eeprom_24lc028bt_i2c); +} + +/** + * @brief 读取数据 + * @param {uint32_t} read_addr + * @param {uint8_t} *data + * @param {uint16_t} length + * @return {*} + * @note + */ +void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length) +{ + // 发送开始信号 + eeprom_24lc028bt_i2c->interface.start(eeprom_24lc028bt_i2c); + // 发送写入地址命令 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, W_ADD_COM); + // 等待写入地址命令响应 + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + + // 发送要读取的地址 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, (uint8_t)read_addr); + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + + // 发送开始信号 + eeprom_24lc028bt_i2c->interface.start(eeprom_24lc028bt_i2c); + // 发送读取地址命令 + eeprom_24lc028bt_i2c->interface.write_byte(eeprom_24lc028bt_i2c, R_ADD_COM); + // 等待读取地址命令响应 + eeprom_24lc028bt_i2c->interface.wait_ack(eeprom_24lc028bt_i2c); + // 循环读取数据 + for (uint16_t i = 0; i < length - 1; i++) + { + // 读取一个字节数据 + *data++ = eeprom_24lc028bt_i2c->interface.read_byte(eeprom_24lc028bt_i2c, TRUE); + } + *data++ = eeprom_24lc028bt_i2c->interface.read_byte(eeprom_24lc028bt_i2c, FALSE); + // 停止I2C总线 + eeprom_24lc028bt_i2c->interface.stop(eeprom_24lc028bt_i2c); +} diff --git a/lib/driver/eeprom_lc02b.h b/lib/driver/eeprom_lc02b.h new file mode 100644 index 0000000..d5edf21 --- /dev/null +++ b/lib/driver/eeprom_lc02b.h @@ -0,0 +1,56 @@ +/** + * @file eeprom_lc02b.h + * @author xxx + * @date 2023-12-27 14:44:02 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __EEPROM_LC02B_H +#define __EEPROM_LC02B_H +#include "main.h" +#include "entity.h" + +#define PRRESSURE_CALIBRATION_ADDRESS 0x00 ///< 压力校准地址 + +/** + * @brief Initializes the LC02B EEPROM module. + */ +void eeprom_lc02b_init(void); + +/** + * @brief Deinitializes the LC02B EEPROM module. + */ +void eeprom_lc02b_dinit(void); + +/** + * @brief Writes data to the LC02B EEPROM module. + * + * @param write_addr The starting address to write the data. + * @param data The pointer to the data to be written. + * @param length The length of the data to be written. + */ +void eeprom_lc02b_write(uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Reads data from the LC02B EEPROM module. + * + * @param read_addr The starting address to read the data. + * @param data The pointer to store the read data. + * @param length The length of the data to be read. + */ +void eeprom_lc02b_read(uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Performs a test on the LC02B EEPROM module. + */ +void eeprom_lc02b_test(void); + +/** + * @brief Gets the LC02B EEPROM status. + * + * This function is used to get the current status of the LC02B EEPROM. + * + * @return TRUE if the LC02B EEPROM is in normal status, FALSE otherwise. + */ +BOOL eeprom_lc02b_status_get(void); +#endif ///< !__EEPROM_LC02B_H diff --git a/lib/driver/eeprom_m95.c b/lib/driver/eeprom_m95.c new file mode 100644 index 0000000..7b134b6 --- /dev/null +++ b/lib/driver/eeprom_m95.c @@ -0,0 +1,331 @@ +/** + * @file eeprom_m95.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现M95 EEPROM相关的读写操作 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "eeprom_m95.h" +#include "spis.h" +#include "delay.h" +#include "entity.h" +#include "board.h" +#include "diagnosis.h" +#include "storage.h" + +#define M95_SPI SPI1 + +#define EEPROM_M95_1_CS_PORT EE1_CS_GPIO_Port +#define EEPROM_M95_1_CS_PIN EE1_CS_Pin +#define EEPROM_M95_2_CS_PORT EE2_CS_GPIO_Port +#define EEPROM_M95_2_CS_PIN EE2_CS_Pin + +// 下面宏定义为2个EEPROM_M95的引脚定义 +#define EEPROM_M95_MOSI_PORT SPI_MOSI_GPIO_Port +#define EEPROM_M95_MOSI_PIN SPI_MOSI_Pin +#define EEPROM_M95_MISO_PORT SPI_MISO_GPIO_Port +#define EEPROM_M95_MISO_PIN SPI_MISO_Pin +#define EEPROM_M95_SCK_PORT SPI_CLK_GPIO_Port +#define EEPROM_M95_SCK_PIN SPI_CLK_Pin + +m95_number_t eeprom_m95s[M95_MAX]; + +/** + * @brief 初始化EEPROM_M95eeprom_m95s + * @param {m95_number_e} num + * @return {*} + * @note 初始化函数对板卡上不同的芯片定义了块大小 + */ +void eeprom_m95_init(m95_number_e num) +{ + DBG_ASSERT(num < M95_MAX __DBG_LINE); + spi_gpio_group_t gpios; + spi_t *eeprom_m95_spi; + spi_normal_config_t cfg; + osel_memset((uint8_t *)&cfg, 0, sizeof(spi_normal_config_t)); + cfg.cmd_rdsr = M95_CMD_RDSR; + cfg.cmd_wrsr = M95_CMD_WRSR; + cfg.cmd_wren = M95_CMD_WREN; + cfg.cmd_wrdi = M95_CMD_WRDI; + cfg.cmd_write = M95_CMD_WRITE; + cfg.cmd_read = M95_CMD_READ; + cfg.dummy_byte = M95_DUMMY_BYTE; + cfg.continuous_write = FALSE; + // 128 byte + if (num == M95_1) + { + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_M95_1_CS_PORT, EEPROM_M95_1_CS_PIN); + + cfg.address_bytes = 3; + cfg.page_size = M95_PAGE_SIZE_256; + cfg.total_size = _M95M02_; + } + // 256 byte + else if (num == M95_2) + { + // 创建CS引脚 + gpios.cs = gpio_create(EEPROM_M95_2_CS_PORT, EEPROM_M95_2_CS_PIN); + + cfg.address_bytes = 3; + cfg.page_size = M95_PAGE_SIZE_256; + cfg.total_size = _M95M02_; + } + else + { + DBG_ASSERT(FALSE __DBG_LINE); + } + + gpios.mosi = gpio_create(EEPROM_M95_MOSI_PORT, EEPROM_M95_MOSI_PIN); + gpios.sck = gpio_create(EEPROM_M95_SCK_PORT, EEPROM_M95_SCK_PIN); + gpios.miso = gpio_create(EEPROM_M95_MISO_PORT, EEPROM_M95_MISO_PIN); + gpios.rst = gpio_create(NULL, 0); + gpios.rdy = gpio_create(NULL, 0); + + // 创建SPI对象 + eeprom_m95_spi = spi_create(SPI_TYPE_NORMAL, gpios, 10); + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + osel_memcpy((uint8_t *)&eeprom_m95_spi->cfg, (uint8_t *)&cfg, sizeof(spi_normal_config_t)); + // 使能SPI + eeprom_m95_spi->interface.hardware_enable(eeprom_m95_spi, M95_SPI); + eeprom_m95s[num].num = num; + eeprom_m95s[num].spi = eeprom_m95_spi; + // 这里需要设置,否则读出的数据不对 + eeprom_m95_spi->interface.u.normal.spi_reset(eeprom_m95_spi); + eeprom_m95_write_protection_close(num); // 关闭写保护 + + // eeprom_m95_test(num); +} + +/** + * @brief 反初始化EEPROM_M95 + * @param {m95_number_e} num + * @return {*} + * @note + */ +void eeprom_m95_dinit(m95_number_e num) +{ + LL_SPI_Disable(M95_SPI); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.mosi->port, eeprom_m95s[num].spi->gpios.mosi->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.miso->port, eeprom_m95s[num].spi->gpios.miso->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.sck->port, eeprom_m95s[num].spi->gpios.sck->pin); + GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.cs->port, eeprom_m95s[num].spi->gpios.cs->pin); +} + +/** + * @brief M95 EEPROM使能 + * @return {*} + * @note + */ +void eeprom_m95_enable(void) +{ + uint16_t count = 100; + LL_SPI_Enable(M95_SPI); + // 判断SPI是否使能成功 + while (LL_SPI_IsEnabled(M95_SPI) != 1) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief M95 EEPROM失能 + * @return {*} + * @note + */ +void eeprom_m95_disable(void) +{ + uint16_t count = 100; + LL_SPI_Disable(M95_SPI); + // 判断SPI是否关闭成功 + while (LL_SPI_IsEnabled(M95_SPI) != 0) + { + if (count-- == 0) + { + return; + } + else + { + __NOP(); + } + } +} + +/** + * @brief 关闭EEPROM M95写保护 + * + * 关闭指定M95 EEPROM的写保护功能。 + * + * @param num 指定M95 EEPROM的编号 + */ +void eeprom_m95_write_protection_close(m95_number_e num) +{ + spi_t *handle = eeprom_m95s[num].spi; + DBG_ASSERT(handle != NULL __DBG_LINE); + eeprom_m95_enable(); + handle->interface.u.normal.spi_write_reg(handle, handle->cfg.cmd_wrsr, 0); + eeprom_m95_disable(); +} + +/** + * @brief 获取M95 EEPROM写保护状态(软件) + * + * 根据传入的M95编号获取其写保护状态。 + * + * @param num M95编号 + * @return 如果M95 EEPROM处于写保护状态,则返回FALSE;否则返回TRUE。 + */ +BOOL eeprom_m95_write_protection_state(m95_number_e num) +{ + spi_t *handle = eeprom_m95s[num].spi; + DBG_ASSERT(handle != NULL __DBG_LINE); + eeprom_m95_enable(); + eeprom_m95s[num].write_protection.data = handle->interface.u.normal.spi_read_reg(handle, handle->cfg.cmd_rdsr); + eeprom_m95_disable(); + + if (eeprom_m95s[num].write_protection.bits.bp0 == 1 || + eeprom_m95s[num].write_protection.bits.bp1 == 1 || + eeprom_m95s[num].write_protection.bits.srwd == 1) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief 读取M95 EEPROM内存数据 + * @param num EEPROM模块编号(0或1) + * @param read_addr 要读取的地址 + * @param data 存储读取数据的缓冲区 + * @param length 要读取的数据长度 + * @return {*} + */ +BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + + spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; // 获取EEPROM模块的SPI配置 + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + // 开启和关闭SPI对读写实时性有影响 + eeprom_m95_enable(); + ret = eeprom_m95_spi->interface.u.normal.spi_read(eeprom_m95_spi, read_addr, data, length); + eeprom_m95_disable(); + return ret; +} + +/** + * @brief 向M95 EEPROM内存写入数据 + * @param num EEPROM模块编号(0或1) + * @param write_addr 要写入的地址 + * @param data 包含要写入数据的缓冲区 + * @param length 要写入的数据长度 + * @return {*} + */ +BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length) +{ + BOOL ret = FALSE; + if (length == 0) + { + return ret; + } + spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; + DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); + // 开启和关闭SPI对读写实时性有影响 + eeprom_m95_enable(); + ret = eeprom_m95_spi->interface.u.normal.spi_write(eeprom_m95_spi, write_addr, data, length); + eeprom_m95_disable(); + return ret; +} + +BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_read(M95_1, addr, buf, size); +} + +BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_write(M95_1, addr, (uint8_t *)buf, size); +} + +BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_read(M95_2, addr, buf, size); +} + +BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size) +{ + return eeprom_m95_write(M95_2, addr, (uint8_t *)buf, size); +} +/** + * @brief 用于M95 EEPROM测试 + * @param {m95_number_e} num + * @return {*} + * @note + */ +BOOL eeprom_m95_test(m95_number_e num) +{ + const uint8_t buf_size = 5; + storage_t *st = storage_init(M95_TEST_PAGE * M95_PAGE_SIZE_256, M95_PAGE_SIZE_256); + DBG_ASSERT(st != NULL __DBG_LINE); + + if (num == M95_1) + { + st->ops.read = eeprom_m95_1_read; + st->ops.write = eeprom_m95_1_write; + } + else + { + st->ops.read = eeprom_m95_2_read; + st->ops.write = eeprom_m95_2_write; + } + uint8_t buf[buf_size]; + uint8_t rbuf[buf_size]; + storage_add_node(st, 0, buf_size); + osel_memset(buf, 0, buf_size); + buf[0] = 0xD5; + buf[1] = 0xC8; + buf[2] = num; + buf[3] = 0xaa; + buf[4] = 0xbb; + storage_write(st, 0, buf); + __NOP(); + storage_read(st, 0, rbuf); + storage_destroy(st); + if (osel_memcmp(buf, rbuf, buf_size) == 0) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 获取EEPROM M95状态 + * + * 获取EEPROM M95设备的工作状态。 + * + * @param num EEPROM M95设备编号 + * + * @return 如果EEPROM M95设备正常工作,返回TRUE;否则返回FALSE + */ +BOOL eeprom_m95_status_get(m95_number_e num) +{ + return TRUE; +} diff --git a/lib/driver/eeprom_m95.h b/lib/driver/eeprom_m95.h new file mode 100644 index 0000000..6b06947 --- /dev/null +++ b/lib/driver/eeprom_m95.h @@ -0,0 +1,166 @@ +/** + * @file eeprom_m95.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 eeprom_m95.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __EEPROM_M95_H +#define __EEPROM_M95_H +#include "main.h" +#include "spis.h" + +#define _M95010_ 128 +#define _M95020_ 256 +#define _M95040_ 512 + +#define _M95080_ 1024 +#define _M95160_ 2048 +#define _M95320_ 4096 +#define _M95640_ 8192 + +#define _M95128_ 16384 +#define _M95256_ 32768 + +#define _M95512_ 65536 ///< 65K +#define _M95M02_ 262144 ///< 256K + +#define _M95_SIZE _M95512_ + +#define M95_CMD_RDSR 0x05 /*!< Read Status Register instruction */ +#define M95_CMD_WRSR 0x01 /*!< Write Status Register instruction */ + +#define M95_CMD_WREN 0x06 /*!< Write enable instruction */ +#define M95_CMD_WRDI 0x04 /*!< Write disable instruction */ + +#define M95_CMD_READ 0x03 /*!< Read from Memory instruction */ +#define M95_CMD_WRITE 0x02 /*!< Write to Memory instruction */ + +///< Instruction available only for the M95_2-D device. +#define M95_CMD_RDID 0x83 /*!< Read identification page*/ +#define M95_CMD_WRID 0x82 /*!< Write identification page*/ +#define M95_CMD_RDLS 0x83 /*!< Reads the Identification page lock status*/ +#define M95_CMD_LID 0x82 /*!< Locks the Identification page in read-only mode*/ + +#define M95_DUMMY_BYTE 0xA5 ///< 虚拟字节 + +#define M95_TEST_PAGE 0 ///< 测试页地址 + +///< 定义存储器大小(Bytes) +typedef enum +{ + M95_PAGE_SIZE_16 = 16, ///< _M95010_ 、_M95020_ 、_M95040_ + M95_PAGE_SIZE_32 = 32, ///< _M95080_ 、_M95160_、_M95320_、_M95640_ + M95_PAGE_SIZE_64 = 64, ///< _M95128_、_M95256_ + M95_PAGE_SIZE_128 = 128, ///< _M95512_ + M95_PAGE_SIZE_256 = 256, ///< _M95M02_ +} m95_page_size_e; + +typedef enum +{ + M95_1, + M95_2, + M95_MAX, +} m95_number_e; ///< 板卡上2块m95芯片定义 + +typedef union +{ + uint8_t data; + struct + { + uint8_t wip : 1; ///< Write in progress + uint8_t wel : 1; ///< Write enable latch + uint8_t bp0 : 1; ///< Block protect 0 + uint8_t bp1 : 1; ///< Block protect 1 + uint8_t reserve : 3; + uint8_t srwd : 1; ///< Status register write protect + } bits; +} m95_write_protection_u; + +typedef struct +{ + m95_number_e num; + m95_write_protection_u write_protection; + spi_t *spi; +} m95_number_t; + +extern m95_number_t eeprom_m95s[M95_MAX]; ///< m95芯片数组 + +/** + * @brief Initializes the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_init(m95_number_e num); + +/** + * @brief Deinitializes the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_dinit(m95_number_e num); + +/** + * @brief Enables the M95 EEPROM module. + */ +extern void eeprom_m95_enable(void); + +/** + * @brief Disables the M95 EEPROM module. + */ +extern void eeprom_m95_disable(void); + +/** + * @brief Closes the write protection of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern void eeprom_m95_write_protection_close(m95_number_e num); + +/** + * @brief write protection state of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_write_protection_state(m95_number_e num); + +/** + * @brief Reads data from the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + * @param read_addr The address to read from. + * @param data The buffer to store the read data. + * @param length The number of bytes to read. + */ +extern BOOL eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length); + +/** + * @brief Writes data to the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + * @param write_addr The address to write to. + * @param data The data to write. + * @param length The number of bytes to write. + */ +extern BOOL eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length); + +/** + * @brief Performs a test on the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_test(m95_number_e num); + +/** + * @brief Gets the status of the M95 EEPROM module. + * + * @param num The M95 EEPROM number. + */ +extern BOOL eeprom_m95_status_get(m95_number_e num); + +extern BOOL eeprom_m95_1_read(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_1_write(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_2_read(uint32_t addr, uint8_t *buf, uint16_t size); +extern BOOL eeprom_m95_2_write(uint32_t addr, uint8_t *buf, uint16_t size); +#endif ///< __EEPROM_M95_H diff --git a/lib/driver/ntc_3950.c b/lib/driver/ntc_3950.c new file mode 100644 index 0000000..161d6e0 --- /dev/null +++ b/lib/driver/ntc_3950.c @@ -0,0 +1,107 @@ +/** + * @file ntc_3950.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现NTC的应用功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "ntc_3950.h" +#define CPU_VREF 2.5 +#define NTC_VREF 2.5 +#define TABLE_SIZE 185 +#define NTC_SERIES_RESISTOR 200000 ///< 200K +#define BASE_TEMP -55 + +const uint32_t _table[TABLE_SIZE] = { + 8989000, 8242680, 7592960, 7021380, 6513750, // -55,-54,-53,-52,-51 + 6059060, 5648680, 5275800, 4935020, 4621990, 4333220, 4065840, 3817520, 3586310, 3370600, // -50,-49,-48,-47,-46,-45,-44,-43,-42,-41 + 3169000, 2980330, 2803600, 2637910, 2482470, 2336580, 2199620, 2071020, 1950230, 1836790, // -40,-39,-38,-37,-36,-35,-34,-33,-32,-31 + 1730230, 1630150, 1536140, 1447840, 1364900, 1287000, 1213820, 1145090, 1080530, 1019890, // -30,-29,-28,-27,-26,-25,-24,-23,-22,-21 + 962912, 909379, 859074, 811797, 767359, 725581, 686296, 649348, 614590, 581883, // -20,-19,-18,-17,-16,-15,-14,-13,-12,-11 + 551100, 522117, 494824, 469113, 444886, 422050, 400518, 380209, 361048, 342963, // -10,-9,-8,-7,-6,-5,-4,-3,-2,-1 + 326560, 309764, 294529, 280131, 266520, 253647, 241470, 229946, 219036, 208706, // 0,1, 2, 3,4,5,6,7,8,9 + 198920, 189647, 180857, 172523, 164618, 157118, 150000, 143243, 136827, 130731, // 10,11 ,12, 13,14,15,16,17,18,19 + 124940, 119435, 114202, 109225, 104491, 100000, 95699, 91617, 87731, 84028, // 20,21, 22, 23,24,25,26,27,28,29 + 80501, 77140, 73936, 70881, 67968, 65188, 62537, 60006, 57590, 55283, // 30,31, 32, 33,34,35,36,37,38,39 + 53080, 50976, 48965, 47044, 45207, 43451, 41771, 40165, 38628, 37157, // 40,41, 42, 43,34,35,36,37,38,39 + 35750, 34402, 33112, 31876, 30692, 29558, 28471, 27429, 26430, 25472, // 50,51, 52, 53,54,55,56,57,58,59 + 24554, 23672, 22827, 22016, 21237, 20489, 19771, 19082, 18420, 17784, // 60,61, 62, 63,64,65,66,67,68,69 + 17172, 16585, 16020, 15477, 14955, 14453, 13970, 13505, 13058, 12628, // 70,71, 72, 73,74,75,76,77,78,79 + 12213, 11815, 11431, 11061, 10705, 10362, 10031, 9712, 9405, 9110, // 80,81, 82, 83,84,85,86,87,88,89 + 8824, 8549, 8284, 8028, 7782, 7544, 7314, 7093, 6879, 6673, // 90,91, 92, 93,94,95,96,97,98,99 + 6474, 6281, 6096, 5916, 5743, 5576, 5415, 5259, 5108, 4963, // 101,102, 103,104,105,106,107,108,109 + 4822, 4687, 4555, 4428, 4306, 4187, 4073, 3962, 3855, 3751, // 111,112, 113,114,115,116,117,118,119 + 3651, 3555, 3461, 3371, 3283, 3199, 3100, 3099, 2899, 2799, // 121,122, 123,124,125,126,127,128,129 +}; + +/** + * @brief 温度查表 + * @param {uint32_t} *list + * @param {uint32_t} rt + * @return {*} + * @note + */ +static uint8_t ntc_lookup(const uint32_t *list, uint32_t rt) +{ + uint8_t middle = 0; + uint8_t indexL = 0; + uint8_t indexR = TABLE_SIZE - 1; + if (rt >= *(list + 0)) + return 0; + if (rt <= *(list + TABLE_SIZE - 1)) + return TABLE_SIZE - 1; + + while ((indexR - indexL) > 1) + { + middle = (indexL + indexR) >> 1; + if (rt == *(list + middle)) + return middle; + else if (rt > *(list + middle)) + indexR = middle; + else if (rt < *(list + middle)) + indexL = middle; + } + return indexL; +} + +/** + * @brief 获取温度值,单位为0.1摄氏度 + * @param {uint16_t} adc采集值 + * @return {float32_u} 温度值 + * @note + */ +float32_u ntc_get_temp(uint16_t adc) +{ + + uint8_t index = 0; + int16_t data = 0; + int16_t t = 0; + int16_t result = 0; + uint32_t rt = 0; + const int16_t base = BASE_TEMP * 10; + float32_u res; + res.f = BASE_TEMP; + + /** + * ad = (4095*rt)/(rt+10000) + * + * rt = (ad*NTC_SERIES_RESISTOR*CPU_VREF)/(NTC_VREF*4095-CPU_VREF*ad) + */ + rt = (adc * NTC_SERIES_RESISTOR * CPU_VREF) / (NTC_VREF * 4095 - CPU_VREF * adc); + index = ntc_lookup(_table, rt); + if (rt >= _table[0]) + return res; + if (rt <= *(_table + TABLE_SIZE - 1)) + { + result = (TABLE_SIZE - 1) * 10 + base; + } + else + { + data = _table[index] - _table[index + 1]; + t = 10 * (_table[index] - rt) / data; + result = base + index * 10 + t; + } + res.f = result / 10.0; + return res; +} diff --git a/lib/driver/ntc_3950.h b/lib/driver/ntc_3950.h new file mode 100644 index 0000000..a5e19f9 --- /dev/null +++ b/lib/driver/ntc_3950.h @@ -0,0 +1,15 @@ +/** + * @file ntc_3950.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 ntc_3950.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __NTC_B3950_H__ +#define __NTC_B3950_H__ +#include "main.h" + +extern float32_u ntc_get_temp(uint16_t adc); ///< 获取温度值 + +#endif ///< __NTC_B3950_H__ diff --git a/lib/driver/rtc_rx8010.c b/lib/driver/rtc_rx8010.c new file mode 100644 index 0000000..42d418f --- /dev/null +++ b/lib/driver/rtc_rx8010.c @@ -0,0 +1,541 @@ +/** + * @file rtc_rx8010.c + * @author xxx + * @date 2023-08-30 08:58:43 + * @brief 用于实现RTC芯片RX8010的应用功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "rtc_rx8010.h" +#include "i2cs.h" +#include "delay.h" + +#define RTC_RX8010_SDA_PORT RTC_SDA_GPIO_Port +#define RTC_RX8010_SDA_PIN RTC_SDA_Pin +#define RTC_RX8010_SCL_PORT RTC_SCL_GPIO_Port +#define RTC_RX8010_SCL_PIN RTC_SCL_Pin + +static i2c_t *rtc; + +/* sec, min, hour, week, day, month, year */ +// static uint8_t calendar[7] = {0, 0, 0, 1, 29, 2, 98}; + +/** + * @brief 从RTC芯片的指定地址读取一个字节数据 + * @param {uint8_t} *read_buf + * @param {uint8_t} addr + * @return {*} + */ +static BOOL rtc_read_byte(uint8_t *read_buf, uint8_t addr) +{ + uint8_t *p = read_buf; + + /* 发送起始信号 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送读地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + /* 读取数据 */ + *p = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 从RTC芯片的指定地址读取若干数据 + * @param {uint8_t} *read_buf + * @param {uint8_t} addr + * @param {int} size + * @return {*} + */ +static BOOL rtc_read_bytes(uint8_t *read_buf, uint8_t addr, int size) +{ + int i = 0; + + /* 发送起始信号 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 此处是写方向,因为要发送读地址 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送读地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 重新发送起始信号。前面的代码的目的向RTC传送地址,下面开始读取数据 */ + rtc->interface.start(rtc); + + /* 发送从机地址 + 读写方向 */ + rtc->interface.write_byte(rtc, RTC_RD_ADDR); /* 此处是读方向,因为要开始读数据了 */ + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 循环读取数据,RTC芯片地址自动自增 */ + for (i = 0; i < size; i++) + { + /* 每读完1个字节后,需要发送Ack, 最后一个字节需要发Nack */ + if (i != (size - 1)) + { + read_buf[i] = rtc->interface.read_byte(rtc, TRUE); /* 读1个字节 */ + } + else + { + read_buf[i] = rtc->interface.read_byte(rtc, FALSE); /* 读1个字节 */ + } + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 向RTC芯片的指定地址写入一个数据 + * @param {uint8_t} data + * @param {uint8_t} addr + * @return {*} + * @note + */ +static BOOL rtc_write_byte(uint8_t data, uint8_t addr) +{ + int retry = 0; + + /* 尝试与RTC芯片建立I2C通讯 */ + for (retry = 0; retry < 100; retry++) + { + rtc->interface.start(rtc); /* 发送起始信号 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ + if (rtc->interface.wait_ack(rtc) == TRUE) + { + break; + } + } + if (retry == 100) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送起始写地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 写入数据 */ + rtc->interface.write_byte(rtc, data); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 向RTC芯片的指定地址写入若干数据 + * @param {uint8_t} *write_buf + * @param {uint8_t} addr + * @param {int} size + * @return {*} + * @note + */ +static BOOL rtc_write_bytes(uint8_t *write_buf, uint8_t addr, int size) +{ + int i = 0; + int retry = 0; + + for (i = 0; i < size; i++) + { + if (i == 0) + { + /* 尝试与RTC芯片建立I2C通讯 */ + for (retry = 0; retry < 100; retry++) + { + rtc->interface.start(rtc); /* 发送起始信号 */ + rtc->interface.write_byte(rtc, RTC_WR_ADDR); /* 发送从机地址 + 读写方向 */ + if (rtc->interface.wait_ack(rtc) == TRUE) + { + break; + } + } + if (retry == 100) + { + rtc->interface.stop(rtc); + return FALSE; + } + + /* 发送起始写地址 */ + rtc->interface.write_byte(rtc, addr); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + } + + /* 循环写入数据,RTC芯片地址自动自增 */ + rtc->interface.write_byte(rtc, write_buf[i]); + if (rtc->interface.wait_ack(rtc) != TRUE) + { + rtc->interface.stop(rtc); + return FALSE; + } + } + + /* 命令执行成功,发送I2C总线停止信号 */ + rtc->interface.stop(rtc); + return TRUE; +} + +/** + * @brief 假读RTC芯片,任意读地址,不判断RTC响应 + * @return {*} + * @note + */ +static void rtc_dummy_read(void) +{ + rtc->interface.start(rtc); + rtc->interface.write_byte(rtc, RTC_WR_ADDR); + rtc->interface.write_byte(rtc, 0x20); + + rtc->interface.start(rtc); + rtc->interface.write_byte(rtc, RTC_RD_ADDR); + rtc->interface.read_byte(rtc, FALSE); + + rtc->interface.stop(rtc); +} + +/** + * @brief 用于检查VLF,寄存器地址:0x1e bit[1] + * @return {uint8_t} 0 = VLF位为0,1 = VLF位为1 + * @note + */ +static uint8_t rtc_check_vlf(void) +{ + uint8_t flag_register = 1; + uint8_t vlf = 0; + + rtc_read_byte(&flag_register, RTC_FLAG_ADDR); + + vlf = (flag_register & 0x02); + if (vlf == 0) + { + return 0; + } + else + { + return 1; + } +} + +/** + * @brief 等待VLF位清除,寄存器地址:0x1e bit[1] + * @return {uint8_t} 0 = 清零成功,1 = 清零失败,2 = 无需清零 + */ +static uint8_t rtc_wait_vlf_clear(void) +{ + uint8_t ret = 1; + uint8_t i = 0; + uint8_t vlf; + + for (i = 0; i < 10; i++) + { + vlf = rtc_check_vlf(); + + if (vlf == 0) + { + ret = ((i > 0) ? 0 : 2); + return ret; + } + + /* 清除VLF */ + rtc_write_byte(0, RTC_FLAG_ADDR); + } + + return ret; +} + +/** + * @brief 复位 + * @return {BOOL} FALSE = 复位成功,TRUE = 复位失败 + */ +static BOOL rtc_soft_reset(void) +{ + BOOL ret = FALSE; + + ret = rtc_write_byte(0x00, 0x1f); + ret = rtc_write_byte(0x80, 0x1f); + ret = rtc_write_byte(0xd3, 0x60); + ret = rtc_write_byte(0x03, 0x66); + ret = rtc_write_byte(0x02, 0x6b); + ret = rtc_write_byte(0x01, 0x6b); + + if (ret == 0) + { + LL_mDelay(2); + } + + return ret; +} + +/** + * @brief 时钟寄存器初始化函数 + * @return {BOOL} TRUE = 初始化成功,FALSE = 初始化失败 + */ +static BOOL rtc_clock_reginit(void) +{ + BOOL ret = FALSE; + + /* set reserve register */ + ret = rtc_write_byte(RTC_REG17_DATA, RTC_REG17_ADDR); + ret = rtc_write_byte(RTC_REG30_DATA, RTC_REG30_ADDR); + ret = rtc_write_byte(RTC_REG31_DATA, RTC_REG31_ADDR); + ret = rtc_write_byte(RTC_IRQ_DATA, RTC_IRQ_ADDR); + /* write 0x04 to reg_0x1d */ + ret = rtc_write_byte(0x04, 0x1d); + /* write 0x00 to reg_0x1e */ + ret = rtc_write_byte(0x00, 0x1e); + /* stop clock */ + ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); + /* set the present time */ + // ret = rtc_write_bytes(calendar, RTC_CLOCK_ADDR, sizeof(calendar)); + /* start clock */ + ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); + + return ret; +} + +/** + * @brief RTC芯片初始化 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_init(void) +{ + i2c_gpio_group_t gpios; + int ret = 1; + gpios.scl = gpio_create(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); + gpios.sda = gpio_create(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); + + rtc = i2c_create(gpios, 10); + + rtc_dummy_read(); + /* wait for VLF bit clear */ + ret = rtc_wait_vlf_clear(); + if (ret == 0) + { + /* software reset */ + ret = rtc_soft_reset(); + if (ret == FALSE) + { + return FALSE; + } + } + else if (ret == 1) + { + return FALSE; + } + + /* register initialize */ + + return rtc_clock_reginit(); +} + +/** + * @brief RTC芯片反初始化 + * @return {*} + * @note + */ +BOOL rtc_dinit(void) +{ + GPIO_SET_ANALOG(RTC_RX8010_SCL_PORT, RTC_RX8010_SCL_PIN); + GPIO_SET_ANALOG(RTC_RX8010_SDA_PORT, RTC_RX8010_SDA_PIN); + return TRUE; +} + +/** + * @brief 从RTC芯片读取时间 + * @param {uint8_t} *read_buf - 接收缓存指针,用于接收读取到的数据 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_get_clock_time(uint8_t *read_buf) +{ + return rtc_read_bytes(read_buf, RTC_CLOCK_ADDR, 7); +} + +/** + * @brief 向RTC芯片写入时间 + * @param {rtc_date} *data - 发送缓存指针,用于存储要发送的数据 + * @return {BOOL} TRUE = 成功,FALSE = 失败 + * @note + */ +BOOL rtc_set_clock_time(rtc_date *data) +{ + BOOL ret = FALSE; + uint8_t tmp[7]; + tmp[0] = data->second; + tmp[1] = data->minute; + tmp[2] = data->hour; + tmp[3] = data->weekday; + tmp[4] = data->day; + tmp[5] = data->month; + tmp[6] = data->year; + + tmp[3] = (rtc_week_e)tmp[3]; // 改成星期几 + + /* stop clock */ + ret = rtc_write_byte(0x40, RTC_CONTROL_ADDR); + /* set the present time */ + ret = rtc_write_bytes(tmp, RTC_CLOCK_ADDR, sizeof(tmp)); + /* start clock */ + ret = rtc_write_byte(0x00, RTC_CONTROL_ADDR); + + return ret; +} + +/** + * 获取实时时钟(RTC)的时间戳 + * + * 该函数从RTC设备中获取当前的日期和时间,并将其转换为自1970年1月1日以来的秒数(时间戳)。 + * 如果无法从RTC设备中获取时间,则返回0。 + * + * @return 返回从1970年1月1日以来的秒数(时间戳),如果无法获取时间则返回0。 + */ +uint32_t rtc_timestamp(void) +{ + BOOL ret = FALSE; + uint8_t tmp[7]; + rtc_date_t date; + rtc_time_t time; + ret = rtc_get_clock_time(tmp); + if (ret == FALSE) + { + return 0; + } + date.year = hex_format_dec(tmp[6]); + date.month = hex_format_dec(tmp[5]); + date.day = hex_format_dec(tmp[4]); + time.hour = hex_format_dec(tmp[2]); + time.minute = hex_format_dec(tmp[1]); + time.second = hex_format_dec(tmp[0]); + return time2stamp(&date, &time); +} + +/** + * @brief 将星期转为星期码 + * @param {uint8_t} *weekday 星期几 + * @return {*} + * @note + */ +void rtc_weekday_convert(uint8_t *weekday) +{ + switch (*weekday) + { + case 1: + *weekday = MON; + break; + case 2: + *weekday = TUE; + break; + case 3: + *weekday = WED; + break; + case 4: + *weekday = THUR; + break; + case 5: + *weekday = FRI; + break; + case 6: + *weekday = SAT; + break; + case 7: + *weekday = SUN; + break; + default: + *weekday = 0; + break; + } +} + +/** + * @brief 将星期码转为星期 + * @param {uint8_t} *weekday 星期码 + * @return {*} + * @note + */ +void rtc_weekday_rconvert(uint8_t *weekday) +{ + switch (*weekday) + { + case MON: + *weekday = 1; + break; + case TUE: + *weekday = 2; + break; + case WED: + *weekday = 3; + break; + case THUR: + *weekday = 4; + break; + case FRI: + *weekday = 5; + break; + case SAT: + *weekday = 6; + break; + case SUN: + *weekday = 7; + break; + default: + *weekday = 0; + break; + } +} diff --git a/lib/driver/rtc_rx8010.h b/lib/driver/rtc_rx8010.h new file mode 100644 index 0000000..5fc384a --- /dev/null +++ b/lib/driver/rtc_rx8010.h @@ -0,0 +1,99 @@ +/** + * @file rtc_rx8010.h + * @author xxx + * @date 2023-08-30 14:05:55 + * @brief 头文件 rtc_rx8010.h + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __RTC_RX8010_H__ +#define __RTC_RX8010_H__ +#include "main.h" + +#define RTC_DEVICE_ADDR 0x32 + +#define RTC_WR_ADDR ((RTC_DEVICE_ADDR << 1) | 0) +#define RTC_RD_ADDR ((RTC_DEVICE_ADDR << 1) | 1) + +#define RTC_FLAG_ADDR 0x1e +#define RTC_CLOCK_ADDR 0x10 +#define RTC_CONTROL_ADDR 0x1f + +#define RTC_REG17_ADDR 0x17 +#define RTC_REG17_DATA 0xd8 + +#define RTC_REG30_ADDR 0x30 +#define RTC_REG30_DATA 0x00 + +#define RTC_REG31_ADDR 0x31 +#define RTC_REG31_DATA 0x08 + +#define RTC_IRQ_ADDR 0x32 +#define RTC_IRQ_DATA 0x00 + +typedef enum +{ + SUN = BIT0, + MON = BIT1, + TUE = BIT2, + WED = BIT3, + THUR = BIT4, + FRI = BIT5, + SAT = BIT6 +} rtc_week_e; ///< 星期码 + +typedef struct +{ + uint8_t year; ///< 7 bit - 1 63 + uint8_t month; ///< 4 bit + uint8_t day; ///< 5 bit + uint8_t weekday; ///< rtc_week_e + uint8_t hour; ///< 5 bit + uint8_t minute; ///< 6 bit + uint8_t second; ///< 6 bit +} rtc_date; + +/** + * @brief Initializes the RTC module. + * @return TRUE if the initialization is successful, FALSE otherwise. + */ +extern BOOL rtc_init(void); + +/** + * @brief Deinitializes the RTC module. + * @return TRUE if the deinitialization is successful, FALSE otherwise. + */ +extern BOOL rtc_dinit(void); + +/** + * @brief Retrieves the current clock time from the RTC module. + * @param read_buf Pointer to the buffer to store the clock time. + * @return TRUE if the clock time is successfully retrieved, FALSE otherwise. + */ +extern BOOL rtc_get_clock_time(uint8_t *read_buf); + +/** + * @brief Sets the clock time in the RTC module. + * @param data Pointer to the RTC date structure containing the new clock time. + * @return TRUE if the clock time is successfully set, FALSE otherwise. + */ +extern BOOL rtc_set_clock_time(rtc_date *data); + +/** + * @brief Retrieves the current alarm time from the RTC module. + * @param read_buf Pointer to the buffer to store the alarm time. + * @return TRUE if the alarm time is successfully retrieved, FALSE otherwise. + */ +extern uint32_t rtc_timestamp(void); +/** + * @brief Converts the weekday value to a human-readable format. + * @param weekday Pointer to the weekday value to be converted. + */ +extern void rtc_weekday_convert(uint8_t *weekday); + +/** + * @brief Converts the weekday value from a human-readable format to the RTC format. + * @param weekday Pointer to the weekday value to be converted. + */ +extern void rtc_weekday_rconvert(uint8_t *weekday); +#endif ///< !__RTC_RX8010_H__ diff --git a/lib/driver/sht40.c b/lib/driver/sht40.c new file mode 100644 index 0000000..20998d5 --- /dev/null +++ b/lib/driver/sht40.c @@ -0,0 +1,160 @@ +#include "sht40.h" +#include "i2cs.h" +#define SHT40_SDA_PORT I2C1_SDA_GPIO_Port +#define SHT40_SDA_PIN I2C1_SDA_Pin +#define SHT40_SCL_PORT I2C1_SCL_GPIO_Port +#define SHT40_SCL_PIN I2C1_SCL_Pin + +#define SHT40_I2C_ADDRESS 0x44 +#define SHT40_MEASURE_CMD 0xFD // 2*8-bit T-data:8-bit CRC:2*8-bit RH-data: 8-bit CRC + +static i2c_t *sht40_i2c; +static uint8_t crc8(uint8_t *data, uint8_t len); + +void sht40_test(void) +{ + float32 temperature = 0; + float32 humidity = 0; + sht40_read(&temperature, &humidity); + + __NOP(); +} +/** + * @brief 初始化 SHT40 传感器 + * + * 该函数用于初始化 SHT40 传感器,包括创建 I2C 通讯所需的 GPIO 引脚和 I2C 通讯对象。 + * + * 初始化完成后,sht40_i2c 变量将用于后续的 SHT40 传感器通信。 + */ +void sht40_init(void) +{ + i2c_gpio_group_t gpios; + gpios.scl = gpio_create(SHT40_SCL_PORT, SHT40_SCL_PIN); + gpios.sda = gpio_create(SHT40_SDA_PORT, SHT40_SDA_PIN); + + sht40_i2c = i2c_create(gpios, 10); + DBG_ASSERT(sht40_i2c != NULL __DBG_LINE); + + // sht40_test(); // 测试 SHT40 +} + +/** + * @brief 初始化 SHT40 传感器 + * + * 此函数用于初始化 SHT40 温湿度传感器,以便进行后续的温度和湿度测量。 + * + * 注意:该函数没有返回值,也不接受任何参数。 + */ +void sht40_dinit(void) +{ + GPIO_SET_ANALOG(SHT40_SDA_PORT, SHT40_SDA_PIN); + GPIO_SET_ANALOG(SHT40_SCL_PORT, SHT40_SCL_PIN); +} + +/** + * @brief 读取温湿度传感器数据 + * + * 从温湿度传感器读取温度和湿度数据,并将读取到的数据存储在传入的浮点数指针指向的位置。 + * + * @param temperature 用于存储读取到的温度值的浮点数指针 + * @param humidity 用于存储读取到的湿度值的浮点数指针 + */ +BOOL sht40_read(float32 *temperature, float32 *humidity) +{ + uint8_t data[6]; + + osel_memset(data, 0, ARRAY_LEN(data)); + // 发送开始信号 + sht40_i2c->interface.start(sht40_i2c); + // 发送写入地址命令 + sht40_i2c->interface.write_byte(sht40_i2c, SHT40_I2C_ADDRESS << 1); + // 等待写入地址命令响应 + if (sht40_i2c->interface.wait_ack(sht40_i2c) == FALSE) + { + return FALSE; + } + // 发送测量命令 + sht40_i2c->interface.write_byte(sht40_i2c, SHT40_MEASURE_CMD); + // 等待测量命令响应 + if (sht40_i2c->interface.wait_ack(sht40_i2c) == FALSE) + { + return FALSE; + } + // 停止I2C总线 + sht40_i2c->interface.stop(sht40_i2c); + + LL_mDelay(10); // 根据 SHT40 数据手册,等待至少 10ms + + // 发送开始信号 + sht40_i2c->interface.start(sht40_i2c); + // 发送写入地址命令 + sht40_i2c->interface.write_byte(sht40_i2c, (SHT40_I2C_ADDRESS << 1) | 1); + // 等待写入地址命令响应 + if (sht40_i2c->interface.wait_ack(sht40_i2c) == FALSE) + { + return FALSE; + } + + for (uint8_t i = 0; i < ARRAY_LEN(data); i++) + { + if (i == 5) + { + data[i] = sht40_i2c->interface.read_byte(sht40_i2c, FALSE); + } + else + { + data[i] = sht40_i2c->interface.read_byte(sht40_i2c, TRUE); + } + } + + // 停止I2C总线 + sht40_i2c->interface.stop(sht40_i2c); + + *temperature = 0; + *humidity = 0; + if (crc8(&data[0], 2) != data[2] || crc8(&data[3], 2) != data[5]) + { + return FALSE; + } + else + { + *temperature = (float32)((uint16_t)data[0] << 8 | data[1]) * 175 / 65535 - 45; + *humidity = (float32)((uint16_t)data[3] << 8 | data[4]) * 125 / 65535 - 6; + if (*humidity > 100) + { + *humidity = 100; + } + if (*humidity < 0) + { + *humidity = 0; + } + + return TRUE; + } +} + +/** + * @brief crc8校验函数,多项式为 x^8 + x^5 + x^4 + 1 + * @param data 要校验的数据 + * @param len 要校验的数据的字节数 + * @retval 校验结果 + * @note 该校验适合SHT3温湿度传感器的数据校验 + */ +static uint8_t crc8(uint8_t *data, uint8_t len) +{ + const uint8_t polynomial = 0x31; + uint8_t crc = 0xFF; + int i, j; + + for (i = 0; i < len; ++i) + { + crc ^= *data++; + + for (j = 0; j < 8; ++j) + { + crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1); + } + } + + return crc; +} diff --git a/lib/driver/sht40.h b/lib/driver/sht40.h new file mode 100644 index 0000000..4a6af98 --- /dev/null +++ b/lib/driver/sht40.h @@ -0,0 +1,9 @@ +#ifndef __SHT40_H +#define __SHT40_H +#include "main.h" + +void sht40_init(void); ///< 初始化 SHT40 传感器 +void sht40_dinit(void); ///< 反初始化 SHT40 传感器 +BOOL sht40_read(float32 *temperature, float32 *humidity); ///< 读取温湿度传感器数据 + +#endif ///< !__SHT40_H diff --git a/lib/driver/ssd1306_oled.c b/lib/driver/ssd1306_oled.c new file mode 100644 index 0000000..a232cae --- /dev/null +++ b/lib/driver/ssd1306_oled.c @@ -0,0 +1,1011 @@ +/** + * @file ssd1306_oled.c + * @author xushenghao + * @brief 0.96 SSD1306 OLED display driver + * @version 0.1 + * @note PB13-SCK PB12-SDA + */ +#include "ssd1306_oled.h" +#include "string.h" + +/************************************6*8的点阵************************************/ +const uint8_t F6x8[][6] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp + 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! + 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " + 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # + 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ + 0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % + 0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & + 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' + 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( + 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) + 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + + 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - + 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / + 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 + 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 + 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 + 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : + 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ; + 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < + 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // = + 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > + 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ? + 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @ + 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C + 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F + 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G + 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H + 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I + 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J + 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K + 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L + 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M + 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P + 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q + 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R + 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S + 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T + 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U + 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V + 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W + 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X + 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y + 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ + 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55 + 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ] + 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ + 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // ' + 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a + 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b + 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c + 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d + 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e + 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f + 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g + 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h + 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i + 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j + 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k + 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l + 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n + 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o + 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p + 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r + 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s + 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t + 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u + 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v + 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w + 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x + 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y + 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines +}; +/****************************************8*16的点阵************************************/ +const uint8_t F8X16[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 + 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1 + 0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2 + 0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00, 0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3 + 0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00, 0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4 + 0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5 + 0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, 0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6 + 0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7 + 0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8 + 0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9 + 0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00, 0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10 + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04, 0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16 + 0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17 + 0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18 + 0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00, 0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19 + 0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20 + 0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00, 0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21 + 0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22 + 0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24 + 0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25 + 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26 + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27 + 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28 + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29 + 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30 + 0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31 + 0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00, 0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32 + 0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37 + 0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38 + 0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40 + 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41 + 0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42 + 0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00, 0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44 + 0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00, 0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45 + 0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08, 0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47 + 0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00, 0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48 + 0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49 + 0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50 + 0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51 + 0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52 + 0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53 + 0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54 + 0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00, 0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55 + 0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08, 0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56 + 0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57 + 0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00, 0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58 + 0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59 + 0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60 + 0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61 + 0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63 + 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69 + 0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71 + 0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72 + 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73 + 0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74 + 0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75 + 0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79 + 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80 + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81 + 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82 + 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83 + 0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84 + 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86 + 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80, 0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87 + 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88 + 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89 + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92 + 0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93 + 0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94 +}; + +const uint8_t LOGO[] = { + 0x00, 0x03, 0x05, 0x09, 0x11, 0xFF, 0x11, 0x89, 0x05, 0xC3, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0xFF, 0x11, 0xAA, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x01, 0x38, 0x44, 0x82, 0x92, + 0x92, 0x74, 0x01, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x44, 0xC7, 0x01, 0x7D, + 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0x7D, 0x7D, 0x7D, 0x7D, 0x01, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, + 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0x00, 0x00, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0xE6, 0x66, 0x20, 0x00, 0x06, 0x06, 0x86, 0x06, + 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x86, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x86, 0x86, 0x86, 0x86, 0x86, 0x80, 0x80, 0x86, 0x86, 0x06, 0x86, 0x86, 0xC0, 0xC0, 0x86, + 0x86, 0x86, 0x06, 0x06, 0xD0, 0x30, 0x76, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0xFE, 0x00, 0x01, + 0x02, 0x00, 0xC4, 0x18, 0x20, 0x02, 0x9E, 0x63, 0xB2, 0x0E, 0x00, 0xFF, 0x81, 0x81, 0xFF, 0x00, + 0x00, 0x80, 0x40, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x23, 0xEA, 0xAA, 0xBF, 0xAA, + 0xEA, 0x03, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0C, 0x08, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81, 0x80, 0x80, 0x81, 0x80, + 0x81, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x09, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, 0x00, 0x1E, 0x21, 0x40, 0x40, 0x50, 0x21, 0x5E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0xC1, 0xFF, + 0xFF, 0xC1, 0xC1, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0xFC, 0xF3, 0xEF, 0xF3, 0xFC, + 0x80, 0xFF, 0x80, 0xEE, 0xEE, 0xEE, 0xF5, 0xFB, 0xFF, 0x9C, 0xBE, 0xB6, 0xB6, 0x88, 0xFF, 0x00, + // end +}; + +static uint8_t _buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 定义屏幕缓冲区 +static uint8_t _buffer_copy[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; // 屏幕缓冲区副本 + +static void i2c_start(void) +{ + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_stop(void) +{ + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); +} + +static BOOL i2c_wait_ack(void) +{ + uint8_t count = 0; + SDA_IN(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(4); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(4); + while (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + count++; + if (count > 250) + { + i2c_stop(); + return FALSE; + } + } + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + return TRUE; +} + +static void i2c_ack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +static void i2c_nack(void) +{ + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + SDA_OUT(); + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); +} + +uint8_t i2c_read_byte(BOOL ack) +{ + uint8_t i = 0, receive = 0; + SDA_IN(); + for (i = 0; i < 8; i++) + { + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + receive <<= 1; + if (GPIO_READ(SSD1306_SDA_PORT, SSD1306_SDA_PIN)) + { + receive++; + } + delay_us(1); + } + + if (!ack) + { + i2c_nack(); + } + else + { + i2c_ack(); + } + + return receive; +} + +void i2c_write_byte(uint8_t data) +{ + uint8_t i = 0; + SDA_OUT(); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + for (i = 0; i < 8; i++) + { + // IIC_SDA=(txd&0x80)>>7; + if ((data & 0x80) >> 7) + GPIO_SET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + else + GPIO_RESET(SSD1306_SDA_PORT, SSD1306_SDA_PIN); + + data <<= 1; + delay_us(2); + GPIO_SET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + GPIO_RESET(SSD1306_SCK_PORT, SSD1306_SCK_PIN); + delay_us(2); + } +} + +static void i2c_write_command(uint8_t command) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_LOW_COLUMN; // Co = 0, D/C# = 0 + dd[1] = command; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); + i2c_stop(); +} + +static void i2c_write_data(uint8_t data) +{ + uint8_t dd[2]; + dd[0] = SSD1306_CMD_SET_START_LINE; // Co = 0, D/C# = 1 + dd[1] = data; + i2c_start(); + i2c_write_byte(SSD1306_I2C_ADDRESS); + i2c_wait_ack(); + + i2c_write_byte(dd[0]); + i2c_wait_ack(); + + i2c_write_byte(dd[1]); + i2c_wait_ack(); +} + +/** + * @brief 设置SSD1306 OLED显示屏上的显示位置 + * + * 该函数用于设置SSD1306 OLED显示屏上的显示位置,通过x和y坐标确定显示位置。 + * + * @param x 要设置的横坐标(0-127) + * @param y 要设置的纵坐标(0-7,对应SSD1306 OLED的8个页面) + */ +void set_position(uint8_t x, uint8_t y) +{ + i2c_write_command(0xb0 + y); + i2c_write_command(((x & 0xf0) >> 4) | 0x10); + i2c_write_command((x & 0x0f) | 0x01); +} + +void ssd1306_test(void) +{ + ssd1306_f8x16_string(0, 0, " TEST"); + ssd1306_f8x16_number(40, 0, -15.26, 2); + ssd1306_f6x8_string_number(0, 3, " @ADC1:", 'V', 24); + ssd1306_update_screen(); +} + +void ssd1306_init(void) +{ + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // display off + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // Set Memory Addressing Mode + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid + i2c_write_command(SSD1306_CMD_COM_SCAN_DEC); // Set COM Output Scan Direction + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //---set low column address + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); //---set high column address + i2c_write_command(SSD1306_CMD_SET_START_LINE); //--set start line address + i2c_write_command(SSD1306_CMD_SET_CONTRAST); //--set contrast control register + i2c_write_command(0xff); // 亮度调节 0x00~0xff + i2c_write_command(0xa1); //--set segment re-map 0 to 127 + i2c_write_command(SSD1306_CMD_NORMAL_DISPLAY); //--set normal display + i2c_write_command(SSD1306_CMD_SET_MULTIPLEX); //--set multiplex ratio(1 to 64) + i2c_write_command(0x3f); // + i2c_write_command(SSD1306_CMD_DISPLAY_ALL_ON_RESUME); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content + i2c_write_command(SSD1306_CMD_SET_DISPLAY_OFFSET); //-set display offset + i2c_write_command(SSD1306_CMD_SET_LOW_COLUMN); //-not offset + i2c_write_command(SSD1306_CMD_SET_DISPLAY_CLOCK_DIV); //--set display clock divide ratio/oscillator frequency + i2c_write_command(0xf0); //--set divide ratio + i2c_write_command(SSD1306_CMD_SET_PRECHARGE); //--set pre-charge period + i2c_write_command(SSD1306_CMD_PAGE_ADDR); // + i2c_write_command(SSD1306_CMD_SET_COM_PINS); //--set com pins hardware configuration + i2c_write_command(0x12); + i2c_write_command(SSD1306_CMD_SET_VCOM_DETECT); //--set vcomh + i2c_write_command(SSD1306_CMD_MEMORY_MODE); // 0x20,0.77xVcc + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); //--set DC-DC enable + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // + i2c_write_command(SSD1306_CMD_DISPLAY_ON); //--turn on oled panel + ssd1306_clear(); + + // ssd1306_test(); +} + +void ssd1306_logo(void) +{ + ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2, LOGO); +} + +void ssd1306_display_on(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_DC_DC_ENABLE); // 开启电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_ON); // OLED唤醒 +} + +void ssd1306_display_off(void) +{ + i2c_write_command(SSD1306_CMD_CHARGE_PUMP); // 设置电荷泵 + i2c_write_command(SSD1306_CMD_SET_HIGH_COLUMN); // 关闭电荷泵 + i2c_write_command(SSD1306_CMD_DISPLAY_OFF); // OLED休眠 +} + +/** + * @brief 更新SSD1306 OLED显示屏的内容 + * + * 此函数将缓冲区中的数据写入SSD1306 OLED显示屏,从而更新显示内容。 + * + * 首先,通过发送命令设置列地址和页地址,然后将缓冲区中的数据逐行写入显示屏。 + * + * @note 在调用此函数之前,需要将需要显示的数据写入缓冲区。 + */ +void ssd1306_update_screen(void) +{ + for (uint8_t i = 0; i < SSD1306_HEIGHT / 8; i++) + { + uint8_t update_needed = 0; + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + if (_buffer[j + i * SSD1306_WIDTH] != _buffer_copy[j + i * SSD1306_WIDTH]) + { + update_needed = 1; + break; + } + } + if (update_needed) + { + i2c_write_command(0xb0 + i); + i2c_write_command(0x01); + i2c_write_command(0x10); + for (uint8_t j = 0; j < SSD1306_WIDTH; j++) + { + i2c_write_data(_buffer[j + i * SSD1306_WIDTH]); + } + } + } + + osel_memcpy(_buffer_copy, _buffer, ARRAY_LEN(_buffer)); +} + +/** + * @brief 填充整个屏幕为指定颜色 + * + * 该函数将 SSD1306 OLED 显示屏的每一个像素点都设置为指定的颜色。 + * + * @param color 颜色值,SSD1306_BLACK 表示关闭像素点(黑色),SSD1306_WHITE 表示打开像素点(白色) + */ +void ssd1306_fill(uint8_t color) +{ + ssd1306_clear_buffer(); + osel_memset(_buffer, color, ARRAY_LEN(_buffer)); + ssd1306_update_screen(); +} + +/** + * @brief 清空SSD1306显示屏 + * + * 该函数通过向SSD1306显示屏发送一系列命令来清空显示内容。 + * + * 首先,通过循环遍历每个页面(SSD1306显示屏有8个页面), + * 对每个页面执行以下操作: + * 1. 发送页面地址命令(0xb0 + y),其中y为当前页面索引。 + * 2. 发送列地址低位命令(0x01)。 + * 3. 发送列地址高位命令(0x10),表示从第一列开始。 + * 4. 循环遍历每一列(SSD1306显示屏的宽度), + * 发送数据0x00以清空该列的内容。 + */ +void ssd1306_clear(void) +{ + ssd1306_clear_buffer(); + ssd1306_update_screen(); +} + +void ssd1306_clear_buffer(void) +{ + osel_memset(_buffer, SSD1306_BLACK, ARRAY_LEN(_buffer)); + osel_memset(_buffer_copy, 0xff, ARRAY_LEN(_buffer_copy)); +} + +/** + * @brief 在指定区域内绘制BMP + * + * 在指定坐标区域内绘制一个BMP,使用SSD1306 OLED显示屏 + * ssd1306_draw_bmp(0, 0, SSD1306_WIDTH, 2); + * @param x0 BMP绘制的起始X坐标 + * @param y0 BMP绘制的起始Y坐标 + * @param x1 BMP绘制的结束X坐标 + * @param y1 BMP绘制的结束Y坐标 + */ +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp) +{ + uint8_t j = 0; + uint8_t x, y; + ssd1306_clear_buffer(); + if (y1 % 8 == 0) + y = y1 / 8; + else + y = y1 / 8 + 1; + for (y = y0; y < y1; y++) + { + set_position(x0, y); + for (x = x0; x < x1; x++) + { + i2c_write_data(bmp[j++]); + } + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示字符串 + * + * 在SSD1306 OLED显示屏上以6x8像素的字体显示给定的字符串。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} + +/** + * @brief 将浮点数显示在SSD1306显示屏上 + * + * 该函数将给定的浮点数num转换为字符串,并将其显示在SSD1306显示屏的指定位置。 + * + * @param x 显示的起始x坐标 + * @param y 显示的起始y坐标 + * @param num 要显示的浮点数 + * @param dot_num 小数点后的位数,0表示不显示小数部分 + */ +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } +} +/** + * @brief 在SSD1306 OLED屏幕上显示字符串和数字 + * + * 该函数用于在SSD1306 OLED屏幕上显示字符串和数字。首先显示一个字符串, + * 然后显示一个浮点数。字符串和数字按照指定的位置和单位显示。 + * + * @param x 起始位置的x坐标 + * @param y 起始位置的y坐标 + * @param ch 要显示的字符串 + * @param unit 数字的单位,例如'm'表示米 + * @param num 要显示的浮点数 + */ +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num) +{ + uint8_t c = 0, i = 0, j = 0; + int8_t a, number[7] = {0, 0, 0, -2, 0, 0, 0}; + uint32_t d; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + d = 1000 * num; + for (a = 0; a < 3; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + for (a = 4; a < 7; a++) + { + number[6 - a] = d % 10; + d = d / 10; + } + if (num >= 100) + { + a = 0; + } + else if (num >= 10) + { + a = 1; + } + else if (num >= 0) + { + a = 2; + } + for (; a < 7; a++) + { + c = number[a] + 16; + if (x > 126) + { + x = 0; + y++; + } + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + x += 6; + j++; + } + for (int h = 0; h < 7; h++) + { + c = unit - 32; + for (i = 0; i < 6; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F6x8[c][i]; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上显示8x16大小的字符串 + * + * 该函数使用8x16字体在SSD1306 OLED显示屏上显示指定的字符串。字符串的字符位置由x和y参数指定。 + * + * @param x 显示字符串的起始x坐标 + * @param y 显示字符串的起始y坐标 + * @param str 要显示的字符串 + */ +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch) +{ + uint8_t c = 0, i = 0, j = 0; + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +/** + * @brief 在SSD1306 OLED显示屏上以8x16像素字体显示浮点数 + * + * 在指定的坐标位置 (x, y) 上,使用8x16像素大小的字体显示浮点数 num,并显示指定数量的小数点 dot_num。 + * + * @param x 显示位置的x坐标 + * @param y 显示位置的y坐标 + * @param num 要显示的浮点数 + * @param dot_num 要显示的小数点数量 + */ +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num) +{ + uint8_t ch[9] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; + uint8_t c = 0, i = 0, j = 0; + if (num < 0) + { + ch[i++] = '-'; + num = -num; + } + if (num > 32000) + return; + + c = 8 - i; + + if (num >= 10000) + { + ch[i++] = num / 10000 + 48; + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 1000) + { + ch[i++] = (int32_t)(num) % 10000 / 1000 + 48; + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 100) + { + ch[i++] = (int32_t)(num) % 1000 / 100 + 48; + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 10) + { + ch[i++] = (int32_t)(num) % 100 / 10 + 48; + ch[i++] = (int32_t)(num) % 10 + 48; + } + else if (num >= 0) + { + ch[i++] = (int32_t)(num) % 10 + 48; + } + if (dot_num > 0 && i < 7) + { + ch[i++] = '.'; + num = num - (int32_t)num; + + if (dot_num == 1 && i < 8) + { + num = num * 10; + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + if (dot_num >= 2 && i < 8) + { + num = num * 100; + ch[i++] = (int32_t)num % 100 / 10 + 48; + if (i < 8) + ch[i++] = (int32_t)(num + 0.5) % 10 + 48; + } + } + + while (ch[j] != '\0') + { + c = ch[j] - 32; + if (x > 120) + { + x = 0; + y++; + } + for (i = 0; i < 8; i++) + _buffer[(y * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i]; + for (i = 0; i < 8; i++) + _buffer[((y + 1) * SSD1306_WIDTH) + x + i] = F8X16[c * 16 + i + 8]; + x += 8; + j++; + } +} + +// 在 SSD1306 显示屏上绘制一个像素 +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color) +{ + if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) + { + // 超出屏幕范围 + return; + } + + // 设置像素颜色 + if (color == SSD1306_WHITE) + { + _buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); + } + else + { + _buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8)); + } +} + +// 在 SSD1306 显示屏上绘制一条线 +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) +{ + int16_t dx, dy, sx, sy, err, e2; + + dx = ABS(x2 - x1); + dy = ABS(y2 - y1); + sx = (x1 < x2) ? 1 : -1; + sy = (y1 < y2) ? 1 : -1; + err = dx - dy; + + while (1) + { + ssd1306_draw_pixel(x1, y1, color); + if (x1 == x2 && y1 == y2) + break; + e2 = err * 2; + if (e2 > -dy) + { + err -= dy; + x1 += sx; + } + if (e2 < dx) + { + err += dx; + y1 += sy; + } + } +} + +// 在 SSD1306 显示屏上绘制一个矩形 +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + // 绘制矩形的四条边 + ssd1306_draw_line(x, y, x + w, y, color); // 上边 + ssd1306_draw_line(x, y + h, x + w, y + h, color); // 下边 + ssd1306_draw_line(x, y, x, y + h, color); // 左边 + ssd1306_draw_line(x + w, y, x + w, y + h, color); // 右边 +} + +// 在 SSD1306 显示屏上绘制一个填充矩形 +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + for (uint8_t i = 0; i < h; i++) + { + ssd1306_draw_line(x, y + i, x + w, y + i, color); + } +} + +// 绘制进度条 +void ssd1306_draw_progress_bar(uint8_t progress) +{ + uint8_t offset_y = 10; + char progress_text[5]; // 用于存储百分比文本 + // 绘制边框 + ssd1306_draw_rect_angle(10, 24 + offset_y, 108, 10, SSD1306_WHITE); + + // 绘制进度条 + ssd1306_fill_rect_angle(12, 26 + offset_y, 4 + progress, 7, SSD1306_WHITE); + + // 显示百分比文本 + snprintf(progress_text, sizeof(progress_text), "%3d%%", progress); + ssd1306_f8x16_string(SSD1306_WIDTH * 0.35, 2, (const uint8_t *)progress_text); + + // 更新显示 + ssd1306_update_screen(); +} + +// 在中间显示文字 +void ssd1306_draw_text_center(uint8_t y, const char *text) +{ + uint8_t x = (SSD1306_WIDTH - strlen(text) * 6) / 2; + ssd1306_f6x8_string(x, y, (const uint8_t *)text); + + // 更新显示 + ssd1306_update_screen(); +} diff --git a/lib/driver/ssd1306_oled.h b/lib/driver/ssd1306_oled.h new file mode 100644 index 0000000..32a3304 --- /dev/null +++ b/lib/driver/ssd1306_oled.h @@ -0,0 +1,80 @@ +#ifndef __SSD1306_OLED_H +#define __SSD1306_OLED_H + +#include "main.h" + +// OLED引脚定义 +#define SSD1306_SDA_PORT OLED_SDA_GPIO_Port +#define SSD1306_SDA_PIN OLED_SDA_Pin +#define SSD1306_SCK_PORT OLDE_SCK_GPIO_Port +#define SSD1306_SCK_PIN OLDE_SCK_Pin + +// I2C地址 +#define SSD1306_I2C_ADDRESS 0x78 +// OLED显示参数 +#define SSD1306_WIDTH 128 +#define SSD1306_HEIGHT 64 +// OLED颜色 +#define SSD1306_WHITE 1 +#define SSD1306_BLACK 0 + +// OLED命令定义 +#define SSD1306_CMD_DISPLAY_OFF 0xAE +#define SSD1306_CMD_DISPLAY_ON 0xAF +#define SSD1306_CMD_SET_CONTRAST 0x81 +#define SSD1306_CMD_DISPLAY_ALL_ON_RESUME 0xA4 +#define SSD1306_CMD_DISPLAY_ALL_ON 0xA5 +#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 +#define SSD1306_CMD_INVERT_DISPLAY 0xA7 +#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3 +#define SSD1306_CMD_SET_COM_PINS 0xDA +#define SSD1306_CMD_SET_VCOM_DETECT 0xDB +#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIV 0xD5 +#define SSD1306_CMD_SET_PRECHARGE 0xD9 +#define SSD1306_CMD_SET_MULTIPLEX 0xA8 +#define SSD1306_CMD_SET_LOW_COLUMN 0x00 +#define SSD1306_CMD_SET_HIGH_COLUMN 0x10 +#define SSD1306_CMD_SET_START_LINE 0x40 +#define SSD1306_CMD_MEMORY_MODE 0x20 +#define SSD1306_CMD_COLUMN_ADDR 0x21 +#define SSD1306_CMD_PAGE_ADDR 0x22 +#define SSD1306_CMD_COM_SCAN_INC 0xC0 +#define SSD1306_CMD_COM_SCAN_DEC 0xC8 +#define SSD1306_CMD_SEG_REMAP 0xA0 +#define SSD1306_CMD_CHARGE_PUMP 0x8D +#define SSD1306_CMD_SET_DC_DC_ENABLE 0x14 + +#define SDA_OUT() \ + { \ + GPIO_SET_OUTPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +#define SDA_IN() \ + { \ + GPIO_SET_INPUT(SSD1306_SDA_PORT, SSD1306_SDA_PIN); \ + } + +// 函数声明 +void ssd1306_init(void); +void ssd1306_logo(void); +void ssd1306_display_on(void); +void ssd1306_display_off(void); +void ssd1306_update_screen(void); + +void ssd1306_fill(uint8_t color); +void ssd1306_clear(void); +void ssd1306_clear_buffer(void); +void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp); +void ssd1306_f6x8_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f6x8_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); +void ssd1306_f6x8_string_number(uint8_t x, uint8_t y, const uint8_t *ch, uint8_t unit, float32 num); +void ssd1306_f8x16_string(uint8_t x, uint8_t y, const uint8_t *ch); +void ssd1306_f8x16_number(uint8_t x, uint8_t y, float32 num, uint8_t dot_num); + +void ssd1306_draw_text_center(uint8_t y, const char *text); +void ssd1306_draw_progress_bar(uint8_t progress); +void ssd1306_fill_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_rect_angle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color); +void ssd1306_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color); +void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t color); +#endif // __SSD1306_OLED_H diff --git a/lib/driver/tmc2240.c b/lib/driver/tmc2240.c new file mode 100644 index 0000000..f691ee0 --- /dev/null +++ b/lib/driver/tmc2240.c @@ -0,0 +1,219 @@ +#include "tmc2240.h" + +tmc2240_t _tmc2240[TMC2240_MAX]; + +static void tmc2240_write(tmc2240_index_e index, uint8_t *data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + tmc->spi->interface.u.normal.spi_send(tmc->spi, data[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_read(tmc2240_index_e index, uint8_t *wdata, uint8_t *rdata) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); + + __NOP(); + __NOP(); + __NOP(); + + tmc->spi->gpios.cs->reset(*tmc->spi->gpios.cs); + for (uint16_t i = 0; i < 5; i++) + { + rdata[i] = tmc->spi->interface.u.normal.spi_send(tmc->spi, wdata[i]); + } + tmc->spi->gpios.cs->set(*tmc->spi->gpios.cs); +} + +static void tmc2240_reg_write(tmc2240_index_e index, uint8_t reg, uint32_t data) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + wdata[0] = TMC2240_HIGHT_BIT | reg; + wdata[1] = (data >> 24) & 0xFF; + wdata[2] = (data >> 16) & 0xFF; + wdata[3] = (data >> 8) & 0xFF; + wdata[4] = data & 0xFF; + tmc2240_write(index, wdata); +} + +static uint32_t tmc2240_reg_read(tmc2240_index_e index, uint8_t reg) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + + uint8_t wdata[5] = {0}; + uint8_t rdata[5] = {0}; + wdata[0] = reg; + tmc2240_read(index, wdata, rdata); + return (rdata[1] << 24) | (rdata[2] << 16) | (rdata[3] << 8) | rdata[4]; +} + +/** + * @brief 配置TMC2240步进电机驱动器 + * + * 该函数用于配置TMC2240步进电机驱动器的各种参数,包括步进电机的工作模式、加减速曲线等。 + * + * @param index TMC2240步进电机驱动器的索引,用于指定要配置的驱动器。 + */ +static void tmc2240_config_write(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->config.gconf.data = 0x00000000; + + tmc->config.chopconf.data = 0x00410153; + tmc->config.chopconf.bits.mres = TMC2240_MRES_8; + + tmc->config.drvconf.data = 0x00000021; + tmc->config.global_scaler.data = 0x00000000; + tmc->config.ihold_irun.data = 0x00071f1f; + tmc->config.pwmconf.data = 0xC44C261E; + tmc->config.gstat.data = 0x00000007; + tmc2240_reg_write(index, TMC2240_GCONF, tmc->config.gconf.data); + tmc2240_reg_write(index, TMC2240_CHOPCONF, tmc->config.chopconf.data); + tmc2240_reg_write(index, TMC2240_DRV_CONF, tmc->config.drvconf.data); + tmc2240_reg_write(index, TMC2240_GLOBAL_SCALER, tmc->config.global_scaler.data); + tmc2240_reg_write(index, TMC2240_IHOLD_IRUN, tmc->config.ihold_irun.data); + tmc2240_reg_write(index, TMC2240_PWMCONF, tmc->config.pwmconf.data); + tmc2240_reg_write(index, TMC2240_GSTAT, tmc->config.gstat.data); +} + +static void _tmc2240_enable(tmc2240_index_e index, BOOL enable) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (enable == TRUE) + { + tmc->en->reset(*tmc->en); + PWM_START(tmc->timer, tmc->time_ch); + } + else + { + tmc->en->set(*tmc->en); + PWM_STOP(tmc->timer, tmc->time_ch); + } +} + +static void _tmc2240_direction(tmc2240_index_e index, tmc2240_direction_e dir) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + if (dir == TMC2240_FORWARD) + { + tmc->dir->reset(*tmc->dir); + } + else + { + tmc->dir->set(*tmc->dir); + } +} + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + tmc->timer = timer; + tmc->time_ch = time_ch; + tmc->spi = spi_create(SPI_TYPE_NORMAL, *gpios, 0); + DBG_ASSERT(tmc->spi != NULL __DBG_LINE); + osel_memset((uint8_t *)&tmc->config, 0, sizeof(tmc2240_config_t)); + osel_memset((uint8_t *)&tmc->read_config, 0, sizeof(tmc2240_config_t)); + tmc->spi->interface.hardware_enable(tmc->spi, SPIx); + { + tmc->default_tm.sysclk = SystemCoreClock / 1000; + tmc->default_tm.psc = PWM_GET_PSC(tmc->timer); + tmc->default_tm.arr = PWM_GET_ARR(tmc->timer); + tmc->default_tm.freq = PWM_GET_FREQ(tmc->timer); + } + tmc->params.percent = TMC2240_PWM_DUTY_DEFAULT; + tmc->params.arr = 0; + tmc->params.freq = 0; + tmc->params.enable = FALSE; + tmc->params.direction = TMC2240_FORWARD; + tmc2240_config_write(index); +} + +tmc2240_t *tmc2240_get(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + return &_tmc2240[index]; +} + +void tmc2240_percent(tmc2240_index_e index, float32 percent) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + PWM_SET_DUTY(tmc->timer, tmc->time_ch, ABS(percent)); +} + +void tmc2240_config_read(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + tmc->read_config.gconf.data = tmc2240_reg_read(index, TMC2240_GCONF); + tmc->read_config.chopconf.data = tmc2240_reg_read(index, TMC2240_CHOPCONF); + tmc->read_config.drvconf.data = tmc2240_reg_read(index, TMC2240_DRV_CONF); + tmc->read_config.global_scaler.data = tmc2240_reg_read(index, TMC2240_GLOBAL_SCALER); + tmc->read_config.ihold_irun.data = tmc2240_reg_read(index, TMC2240_IHOLD_IRUN); + tmc->read_config.pwmconf.data = tmc2240_reg_read(index, TMC2240_PWMCONF); + tmc->read_config.gstat.data = tmc2240_reg_read(index, TMC2240_GSTAT); + tmc->data.tmc2240_adc_temp = tmc2240_reg_read(index, TMC2240_ADC_TEMP); + tmc->data.tmc2240_temperature = (float32)(tmc->data.tmc2240_adc_temp - 2038) / 7.7; +} + +void tmc2240_process(tmc2240_index_e index) +{ + DBG_ASSERT(index < TMC2240_MAX __DBG_LINE); + tmc2240_t *tmc = &_tmc2240[index]; + DBG_ASSERT(tmc != NULL __DBG_LINE); + + _tmc2240_enable(index, tmc->params.enable); + _tmc2240_direction(index, tmc->params.direction); + + if (PWM_GET_ARR(tmc->timer) != tmc->params.arr) + { + if (tmc->params.arr == 0) + { + tmc->params.arr = tmc->default_tm.arr; + } + PWM_SET_ARR(tmc->timer, tmc->params.arr); + tmc->params.freq = PWM_GET_FREQ(tmc->timer); + } + + tmc2240_percent(index, tmc->params.percent); +} diff --git a/lib/driver/tmc2240.h b/lib/driver/tmc2240.h new file mode 100644 index 0000000..47709d2 --- /dev/null +++ b/lib/driver/tmc2240.h @@ -0,0 +1,356 @@ +/** + * @file tmc2240.h + * @author xushenghao + * @brief TMC2240驱动头文件 + * @version 0.1 + * @note + * 1. 芯片VM需要供电,否则SPI无法正常通信 + */ + +#ifndef __TMC2240_H +#define __TMC2240_H +#include "main.h" +#include "spis.h" + +#define TMC2240_PWM_DUTY_DEFAULT 50 // PWM默认占空比 + +/* +0x00 = 0x00002108 ;; writing GCONF @ address 0=0x00 with 0x00002108=8456=0.0 +0x03 = 0x00000000 ;; writing SLAVECONF @ address 1=0x03 with 0x00000000=0=0.0 +0x04 = 0x4001682C ;; writing IOIN @ address 2=0x04 with 0x4001682C=1073834028=0.0 +0x0A = 0x00000021 ;; writing DRV_CONF @ address 3=0x0A with 0x00000021=33=0.0 +0x0B = 0x00000000 ;; writing GLOBAL_SCALER @ address 4=0x0B with 0x00000000=0=0.0 +0x10 = 0x00001208 ;; writing IHOLD_IRUN @ address 5=0x10 with 0x00001208=4616=0.0 +0x11 = 0x00000000 ;; writing TPOWERDOWN @ address 6=0x11 with 0x00000000=0=0.0 +0x13 = 0x00000000 ;; writing TPWMTHRS @ address 7=0x13 with 0x00000000=0=0.0 +0x14 = 0x000003BE ;; writing TCOOLTHRS @ address 8=0x14 with 0x000003BE=958=0.0 +0x15 = 0x00000000 ;; writing THIGH @ address 9=0x15 with 0x00000000=0=0.0 +0x2D = 0x00000000 ;; writing DIRECT_MODE @ address 10=0x2D with 0x00000000=0=0.0 +0x38 = 0x00000000 ;; writing ENCMODE @ address 11=0x38 with 0x00000000=0=0.0 +0x39 = 0x00000000 ;; writing X_ENC @ address 12=0x39 with 0x00000000=0=0.0 +0x3A = 0x00010000 ;; writing ENC_CONST @ address 13=0x3A with 0x00010000=65536=0.0 +0x52 = 0x0B920F25 ;; writing OTW_OV_VTH @ address 14=0x52 with 0x0B920F25=194121509=0.0 +0x60 = 0xAAAAB554 ;; writing MSLUT[0] @ address 15=0x60 with 0xAAAAB554=0=0.0 +0x61 = 0x4A9554AA ;; writing MSLUT[1] @ address 16=0x61 with 0x4A9554AA=1251300522=0.0 +0x62 = 0x24492929 ;; writing MSLUT[2] @ address 17=0x62 with 0x24492929=608774441=0.0 +0x63 = 0x10104222 ;; writing MSLUT[3] @ address 18=0x63 with 0x10104222=269500962=0.0 +0x64 = 0xFBFFFFFF ;; writing MSLUT[4] @ address 19=0x64 with 0xFBFFFFFF=0=0.0 +0x65 = 0xB5BB777D ;; writing MSLUT[5] @ address 20=0x65 with 0xB5BB777D=0=0.0 +0x66 = 0x49295556 ;; writing MSLUT[6] @ address 21=0x66 with 0x49295556=1227445590=0.0 +0x67 = 0x00404222 ;; writing MSLUT[7] @ address 22=0x67 with 0x00404222=4211234=0.0 +0x68 = 0xFFFF8056 ;; writing MSLUTSEL @ address 23=0x68 with 0xFFFF8056=0=0.0 +0x69 = 0x00F70000 ;; writing MSLUTSTART @ address 24=0x69 with 0x00F70000=16187392=0.0 +0x6C = 0x00410153 ;; writing CHOPCONF @ address 25=0x6C with 0x00410153=4260179=0.0 +0x6D = 0x00040000 ;; writing COOLCONF @ address 26=0x6D with 0x00040000=262144=0.0 +0x70 = 0xC44C001E ;; writing PWMCONF @ address 27=0x70 with 0xC44C001E=0=0.0 +0x74 = 0x00000000 ;; writing SG4_THRS @ address 28=0x74 with 0x00000000=0=0.0 +*/ + +#define TMC2240_GCONF 0x00 +#define TMC2240_GSTAT 0x01 +#define TMC2240_IFCNT 0x02 +#define TMC2240_SLAVECONF 0x03 +#define TMC2240_IOIN 0x04 +#define TMC2240_DRV_CONF 0x0A +#define TMC2240_GLOBAL_SCALER 0x0B + +#define TMC2240_IHOLD_IRUN 0x10 +#define TMC2240_TPOWERDOWN 0x11 +#define TMC2240_TSTEP 0x12 +#define TMC2240_TPWMTHRS 0x13 +#define TMC2240_TCOOLTHRS 0x14 +#define TMC2240_THIGH 0x15 + +#define TMC2240_DIRECT_MODE 0x2D + +#define TMC2240_ENCMODE 0x38 +#define TMC2240_XENC 0x39 +#define TMC2240_ENC_CONST 0x3A +#define TMC2240_ENC_STATUS 0x3B +#define TMC2240_ENC_LATCH 0x3C + +#define TMC2240_ADC_VSUPPLY_AIN 0x50 +#define TMC2240_ADC_TEMP 0x51 +#define TMC2240_OTW_OV_VTH 0x52 + +#define TMC2240_MSLUT0 0x60 +#define TMC2240_MSLUT1 0x61 +#define TMC2240_MSLUT2 0x62 +#define TMC2240_MSLUT3 0x63 +#define TMC2240_MSLUT4 0x64 +#define TMC2240_MSLUT5 0x65 +#define TMC2240_MSLUT6 0x66 +#define TMC2240_MSLUT7 0x67 +#define TMC2240_MSLUTSEL 0x68 +#define TMC2240_MSLUTSTART 0x69 +#define TMC2240_MSCNT 0x6A +#define TMC2240_MSCURACT 0x6B +#define TMC2240_CHOPCONF 0x6C +#define TMC2240_COOLCONF 0x6D +#define TMC2240_DCCTRL 0x6E +#define TMC2240_DRVSTATUS 0x6F + +#define TMC2240_PWMCONF 0x70 +#define TMC2240_PWMSCALE 0x71 +#define TMC2240_PWM_AUTO 0x72 +#define TMC2240_SG4_THRS 0x74 +#define TMC2240_SG4_RESULT 0x75 +#define TMC2240_SG4_IND 0x76 + +#define TMC2240_HIGHT_BIT 0x80 + +typedef enum +{ + TMC2240_1, + TMC2240_MAX, +} tmc2240_index_e; + +typedef enum +{ + TMC2240_FORWARD, // 正转 + TMC2240_BACKWARD, // 反转 +} tmc2240_direction_e; + +typedef enum +{ + TMC2240_MRES_256, + TMC2240_MRES_128, + TMC2240_MRES_64, + TMC2240_MRES_32, + TMC2240_MRES_16, + TMC2240_MRES_8, + TMC2240_MRES_4, + TMC2240_MRES_2, + TMC2240_MRES_1, // FULL STEP +} tmc2240_mres_e; + +// 0x00 GCONF +typedef union +{ + uint32_t data; + struct + { + + uint32_t reserved1 : 1; + /** + * 停止之前的步骤执行超时检测。 + * 0x0:正常时间:2^20个时钟 + * 0x1:短时间:2^18个时钟 + */ + uint32_t fast_standstill : 1; + /** + * 启用StealthChop2模式。 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能(取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD = 时为开状态 仅限额定电流。 + */ + uint32_t en_pwm_mode : 1; + /** + * 启用StealthChop2的步进输入筛选 + * 0x0:无StealthChop2 + * 0x1:StealthChop2电压PWM模式使能 + (取决于速度阈值)。从关闭状态切换 + 在静止状态下和在lHOLD=时为开状态 + 仅限额定电流。 + */ + uint32_t multistep_filt : 1; + /** + * 更改电机方向/方向标志 + * 0x0:默认电机方向 + * 0x1:电机方向相反 + */ + uint32_t shaft : 1; + uint32_t diag0_error : 1; + uint32_t diag0_otpw : 1; + uint32_t diag0_stall : 1; + uint32_t diag1_stall : 1; + uint32_t diag1_index : 1; + uint32_t diag1_onstate : 1; + uint32_t reserved2 : 1; + uint32_t diag0_pushpull : 1; + uint32_t diag1_pushpull : 1; + uint32_t small_hysteresis : 1; + /** + * 电机硬停止功能启用。 + * 0x0:正常运行 + * 0x1:紧急停止:ENCA停止定序器 + 当绑得很高时(不执行任何步骤 + 定序器、电机进入停顿状态)。 + */ + uint32_t stop_enable : 1; + /** + * 通过以下方式启用直接motpr相电流控制 + * 0x0:正常运行 + * 0x1:电机线圈电流和极性直接 + 通过串口编程:寄存器 + 直接模式(0x2D)指定带符号线圈A + 电流(位8..0)和线圈B电流(位24..16)。在……里面 + 在此模式下,电流按lHOLD设置进行定标。 + 基于速度的StealthChop2电流调节 + 在此模式下不可用。自动的 + StealthChop2电流调节仅适用于 + 步进电机速度低。 + */ + uint32_t direct_mode : 1; + } bits; +} gconf_u; + +// 0x01 GSTAT +typedef union +{ + uint32_t data; + struct + { + uint32_t reset : 1; + uint32_t drv_err : 1; + uint32_t uv_cp : 1; + uint32_t register_reset : 1; + uint32_t vm_uvlo : 1; + } bits; +} gstat_u; + +// 0x0A DRVCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t current_range : 2; // 0-1 + uint32_t reserved1 : 2; // 2-3 + uint32_t slope_control : 2; // 4-5 + } bits; +} drvconf_u; + +// 0x0B GLOBAL_SCALER +typedef union +{ + uint32_t data; + struct + { + uint32_t global_scale : 8; // 0-7 + } bits; +} global_scaler_u; + +// 0x10 IHOLD_IRUN +typedef union +{ + uint32_t data; + struct + { + uint32_t ihold : 5; // 0-4 + uint32_t reserved1 : 3; // 5-7 + uint32_t irun : 5; // 8-12 + uint32_t reserved2 : 3; // 13-15 + uint32_t iholddelay : 4; // 16-19 + uint32_t reserved3 : 4; // 20-23 + uint32_t irundelay : 4; // 24-27 + } bits; +} ihold_irun_u; + +// 0x6C CHOPCONF +typedef union +{ + uint32_t data; + struct + { + uint32_t toff : 4; // 0-3 + uint32_t hstrt : 3; // 4-6 + uint32_t hend : 4; // 7-10 + uint32_t fd3 : 1; // 11 + uint32_t disfdcc : 1; // 12 + uint32_t reserved1 : 1; + uint32_t chm : 1; // 14 + uint32_t tbl : 2; // 15-16 + uint32_t reserved2 : 1; + uint32_t vhighfs : 1; // 18 + uint32_t vhighchm : 1; // 19 + uint32_t tpfd : 4; // 20-23 + uint32_t mres : 4; // 24-27 + uint32_t intpol : 1; // 28 + uint32_t dedge : 1; // 29 + uint32_t diss2g : 1; // 30 + uint32_t diss2vs : 1; // 31 + } bits; +} chopconf_u; + +// 0x70 PWMCONF +typedef union +{ + uint32_t data; + struct + { + /** + * 用户定义的 PWM 幅度偏移(0-255)与静止状态下的全电机电流(CS_ACTUAL=31)相关。 (重置默认值=30) 使用 PWM_OFS 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_OFS 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 PWM_OFS = 0 将禁用将电机电流缩放到低于电机特定的较低测量阈值。此设置应仅在某些条件下使用, + * 例如当电源电压可以上下变化两倍或更多时。它可以防止电机超出调节范围,但也会防止电流降到调节限制以下。 PWM_OFS > 0 允许自动缩放到低 PWM 占空比, + * 甚至低于较低的调节阈值。这允许基于实际(保持)电流比例(寄存器 IHOLD_IRUN)的低(静止)电流设置。 + */ + uint32_t pwm_ofs : 8; + /** + * PWM 幅度的速度依赖梯度: PWM_GRAD x 256 / TSTEP 此值加到 PWM_OFS 以补偿速度依赖的电机反电动势。 使用 PWM_GRAD 作为自动缩放的初始值, + * 以加快自动调谐过程。为此,请将 PWM_GRAD 设置为确定的、特定于应用的值,并将 pwm_autoscale 设置为 0。之后,将 pwm_autoscale 设置为 1。 + * 完成后启用 StealthChop2。 提示: 初始调谐后,可以从 PWM_GRAD_AUTO 中读取所需的初始值。 + */ + uint32_t pwm_grad : 8; + uint32_t pwm_freq : 2; + uint32_t pwm_autoscale : 1; + uint32_t pwm_autograd : 1; + } bits; +} pwmconf_u; + +typedef struct +{ + gconf_u gconf; // 0x00 GCONF + gstat_u gstat; // 0x01 GSTAT + drvconf_u drvconf; // 0x0A DRVCONF + global_scaler_u global_scaler; // 0x0B GLOBAL_SCALER + ihold_irun_u ihold_irun; // 0x10 IHOLD_IRUN + chopconf_u chopconf; // 0x6C CHOPCONF + pwmconf_u pwmconf; // 0x70 PWMCONF +} tmc2240_config_t; + +typedef struct +{ + gpio_t *en; ///< EN_PIN + gpio_t *dir; ///< DIR_PIN + TIM_TypeDef *timer; + uint32_t time_ch; + spi_t *spi; + + tmc2240_config_t config; + tmc2240_config_t read_config; + + uint32_t step; + __IO uint32_t step_count; + + // PRIVATE + struct + { + uint32_t sysclk; + uint32_t psc; // 预分频系数 + uint16_t arr; // 自动重装值 auto reload value + uint32_t freq; + } default_tm; + struct + { + BOOL enable; // 使能 + tmc2240_direction_e direction; + float32 percent; + uint16_t arr; + uint32_t freq; + } params; + + struct + { + uint16_t tmc2240_adc_temp; + float32 tmc2240_temperature; + } data; +} tmc2240_t; + +void tmc2240_init(tmc2240_index_e index, SPI_TypeDef *SPIx, TIM_TypeDef *timer, uint32_t time_ch, spi_gpio_group_t *gpios); +tmc2240_t *tmc2240_get(tmc2240_index_e index); + +void tmc2240_process(tmc2240_index_e index); +void tmc2240_config_read(tmc2240_index_e index); +#endif // __TMC2240_H diff --git a/lib/examples/Makefile b/lib/examples/Makefile new file mode 100644 index 0000000..ac0de08 --- /dev/null +++ b/lib/examples/Makefile @@ -0,0 +1,86 @@ +# 变量BIN: 给定的是我们想要生成的可执行文件的名称 +BIN = run.exe +SO = lib.dll + +# 变量SRC中给的是所有的想要编译的.c源文件,与makefile在同一目录下可直接写(如这里的main.c),否则需要写明相对路径(如这里的其余源文件都在目录src下)。 +# 多文件时,选择用"\"进行分行处理 +SRC = \ + ../src/malloc.c \ + ../src/sqqueue.c \ + ../src/mlist.c \ + ../src/debug.c \ + ../src/data_analysis.c \ + ../src/filter.c \ + ../src/clist.c \ + ../src/aes.c \ + ../src/cmac.c \ + ../src/lib.c + +EXAMPLE = \ + ./simple_clist.c \ + ./simple_data_analysis.c \ + ./simple_sqqueue.c \ + ./simple_aes.c \ + ./simple_cmac.c + +CPLUS_INCLUDE_PATH= -I ../inc + +# 变量CC:给定编译器名gcc +# 变量CFLAGS:传给编译器的某些编译参数,看需求添加 +CC = gcc +CFLAGS = -m32 -std=c99 +# 变量GDB:给定debugger名gdb +# 变量RM:给定删除文件方式,用于后面删除所有编译所得的.o文件,linux下使用rm -rf +GDB = gdb +RM = rm -rf +# 变量OBJS:将变量SRC中所有的.c文件替换成以.o结尾,即将.c源文件编译成.o文件 +OBJS = $(SRC:%.c=%.o) +EXAPMLES = $(EXAMPLE:%.c=%.o) + +$(SO): $(OBJS) $(EXAPMLES) + + +# pull in dependencies for .o files +-include $(OBJS:.o=.d) + +%.o: %.c + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) -c $< -o $@ + +.PHONY: all clean clist data_analysis + +all: $(SO) + +rm: + $(RM) $(OBJS) + +#简单链表 +clist: $(SO) + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_clist.o -o $(BIN) + $(RM) $(OBJS) $(EXAPMLES) + +#数据分析器 +data_analysis: $(SO) + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_data_analysis.o -o $(BIN) + $(RM) $(OBJS) $(EXAPMLES) + +#队列 +sqqueue: $(SO) + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_sqqueue.o -o $(BIN) + $(RM) $(OBJS) $(EXAPMLES) + +#aes加密 +aes: $(SO) + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_aes.o -o $(BIN) + $(RM) $(OBJS) $(EXAPMLES) + +#cmac类CRC +cmac: $(SO) + $(CC) $(CPLUS_INCLUDE_PATH) $(CFLAGS) $(OBJS) ./simple_cmac.o -o $(BIN) + $(RM) $(OBJS) $(EXAPMLES) + +#运行程序 +run: + ./run.exe + +clean: + $(RM) $(OBJS) $(EXAPMLES) $(BIN) diff --git a/lib/examples/simple_aes.c b/lib/examples/simple_aes.c new file mode 100644 index 0000000..fb4fd5c --- /dev/null +++ b/lib/examples/simple_aes.c @@ -0,0 +1,40 @@ +#include "../inc/data_type_def.h" +#include "../inc/log.h" +#include "../inc/osel_arch.h" +#include "../inc/aes.h" + +// 全局变量 +static aes_context AesContext; // 密钥表 +static uint8_t aBlock[] = {0x00, 0x00, 0x00, 0xcc, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 数据块 +static uint8_t sBlock[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 存放输出结果 + +int32_t main(void) +{ + uint8_t buf[16] = {0x00}; + uint8_t size = ARRAY_LEN(buf); + uint8_t key[] = { + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥 + + // 初始化密文 + for (int i = 0; i < size; i++) + { + buf[i] = i; + } + + // 设置预密钥 + osel_memset(AesContext.ksch, 0, ARRAY_LEN(AesContext.ksch)); + aes_set_key(key, 16, &AesContext); + + // 加密 + osel_memcpy(aBlock, buf, size); + aes_encrypt(aBlock, sBlock, &AesContext); + LOG_HEX(sBlock, ARRAY_LEN(sBlock)); // 打印加密结果:50 fe 67 cc 99 6d 32 b6 da 09 37 e9 9b af ec 60 + + // 解密 + osel_memcpy(aBlock, sBlock, size); + aes_decrypt(aBlock, sBlock, &AesContext); + LOG_HEX(sBlock, ARRAY_LEN(sBlock)); // 打印解密结果:00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f +} diff --git a/lib/examples/simple_clist.c b/lib/examples/simple_clist.c new file mode 100644 index 0000000..3254b07 --- /dev/null +++ b/lib/examples/simple_clist.c @@ -0,0 +1,52 @@ +#include "../inc/data_type_def.h" +#include "../inc/clist.h" + +int32_t main(void) +{ + clist_node_t *head = NULL; // 创建头指针,初始化为NULL + clist_init(&head); // 初始化指针(可有可无) + + // 1:添加数据 + for (int32_t i = 0; i < 30; i++) + { + if (i > 10) + clist_push_front(&head, (cnode)i); // 头部插入 + else + clist_push_back(&head, (cnode)i); // 尾部插入 + } + + LOG_PRINT("\n 1: count:%d \n", clist_node_count(head)); // 获取链表节点数,打印 + clist_print(head); // 打印链表 + + // 2:删除数据 + for (int32_t i = 0; i < 10; i++) + { + if (i > 5) + clist_pop_back(&head); // 删除尾部 + else + clist_pop_front(&head); // 头部删除 + } + LOG_PRINT("\n 2: count:%d \n", clist_node_count(head)); + clist_print(head); + + // 3:插入数据 + clist_insert(&head, 5, (cnode)1111); + clist_insert_for_node(&head, head->Next->Next->Next->Next->Next, (cnode)10000); + clist_insert(&head, 1000, (cnode)2222); // 无效插入 + LOG_PRINT("\n 3: count:%d \n", clist_node_count(head)); + clist_print(head); + + // 4:删除指定节点 + clist_remove(&head, (cnode)5); + clist_erase_for_node(&head, head->Next->Next); + clist_remove(&head, (cnode)1000); // 无效删除 + clist_print(head); + LOG_PRINT("\n 4: count:%d \n", clist_node_count(head)); + clist_print(head); + + // 5:删除所有节点 + clist_destroy(&head); + LOG_PRINT("\n 5: count:%d ", clist_node_count(head)); + clist_print(head); + return 0; +} diff --git a/lib/examples/simple_cmac.c b/lib/examples/simple_cmac.c new file mode 100644 index 0000000..a96726f --- /dev/null +++ b/lib/examples/simple_cmac.c @@ -0,0 +1,33 @@ +#include "../inc/data_type_def.h" +#include "../inc/log.h" +#include "../inc/osel_arch.h" +#include "../inc/cmac.h" + +static uint8_t key[] = { + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥 +int32_t main(void) +{ + uint8_t *p; + uint8_t buffer[16] = {0x00}; + uint32_t size = ARRAY_LEN(buffer); + // 初始化需要校验的数据 + for (int i = 0; i < size; i++) + { + buffer[i] = i; + } + uint8_t mic[16]; // 存放生成校验数据的数组 + AES_CMAC_CTX AesCmacCtx[1]; // 密钥扩展表 + AES_CMAC_Init(AesCmacCtx); // 完成密钥扩展表的初始化 + + AES_CMAC_SetKey(AesCmacCtx, key); // 完成密钥扩展表数据 + + AES_CMAC_Update(AesCmacCtx, buffer, size & 0xFF); // 完成数据的奇偶校验 + + AES_CMAC_Final(mic, AesCmacCtx); // 生成16个字节的校验表 + + uint32_t xor_vol = (uint32_t)((uint32_t)mic[3] << 24 | (uint32_t)mic[2] << 16 | (uint32_t)mic[1] << 8 | (uint32_t)mic[0]); // 取表4个字节作为校验码 + + p = (uint8_t *)&xor_vol; + LOG_HEX(p, 4); // 打印结果:5c 7e fb 43 +} diff --git a/lib/examples/simple_cmd.c b/lib/examples/simple_cmd.c new file mode 100644 index 0000000..563e759 --- /dev/null +++ b/lib/examples/simple_cmd.c @@ -0,0 +1,26 @@ +#include "../inc/data_type_def.h" +#include "../inc/log.h" +#include "../inc/cmd.h" + +void at_name_req(void) +{ + LOG_PRINT("name:cmd\n"); +} + +void at_version_req(void) +{ + LOG_PRINT("version:1.0\n"); +} + +REGISTER_CMD(NAME, at_name_req, at name); +REGISTER_CMD(VERSION, at_version_req, at version); + +int32_t main(void) +{ + cmd_init(); + + cmd_parsing("TEST"); + cmd_parsing("NAME"); + cmd_parsing("VERSION"); + return 0; +} diff --git a/lib/examples/simple_data_analysis.c b/lib/examples/simple_data_analysis.c new file mode 100644 index 0000000..41d38ad --- /dev/null +++ b/lib/examples/simple_data_analysis.c @@ -0,0 +1,178 @@ +#include "../inc/data_type_def.h" +#include "../inc/log.h" +#include "../inc/osel_arch.h" +#include "../inc/data_analysis.h" +#define UART_RXSIZE (254U) +#define UART_DATA_ANALYSIS_PORT_1 DATA_1 +#define UART_DATA_ANALYSIS_PORT_2 DATA_2 + +static data_interupt_cb_t uart_data_analysis_cb = NULL; // 数据源中断回调函数 + +static void data_analysis_event1(void) +{ + uint8_t frame[UART_RXSIZE]; + uint8_t data_head[3]; + uint8_t crc[2]; + uint16_t frame_len, out_frame_len; + data_read(UART_DATA_ANALYSIS_PORT_1, &data_head[0], 3); + osel_memcpy((uint8_t *)&frame_len, &data_head[1], 2); + + frame_len = B2S_UINT16(frame_len) - 2; // 报文长度包含帧长,这里需要减2 + if (frame_len > UART_RXSIZE) + { + lock_data(UART_DATA_ANALYSIS_PORT_1); + unlock_data(UART_DATA_ANALYSIS_PORT_1); + return; + } + + out_frame_len = data_read(UART_DATA_ANALYSIS_PORT_1, frame, (uint16_t)frame_len); + if (out_frame_len != frame_len) + { + return; + } + out_frame_len = out_frame_len - 1; // 报文中包含帧尾,这里需要减1 + + // 校验CRC_16 + uint16_t crc_16 = 0; + uint16_t crc16 = crc16_compute(&frame[0], out_frame_len - 2); + osel_memcpy(&crc[0], &frame[out_frame_len - 2], 2); + crc_16 = BUILD_UINT16(crc[1], crc[0]); + if (crc16 != crc_16) + { + return; + } + // CRC校验通过后将数据长度-2 + out_frame_len -= 2; + + LOG_PRINT("data_analysis_event1 ok:"); + LOG_HEX(frame, out_frame_len); +} + +static void data_analysis_event2(void) +{ + uint8_t frame[UART_RXSIZE]; + uint8_t data_head[4]; + uint8_t crc[2]; + uint16_t frame_len, out_frame_len; + data_read(UART_DATA_ANALYSIS_PORT_2, &data_head[0], 4); + osel_memcpy((uint8_t *)&frame_len, &data_head[2], 2); + frame_len = B2S_UINT16(frame_len); + if (frame_len > UART_RXSIZE) + { + lock_data(UART_DATA_ANALYSIS_PORT_2); + unlock_data(UART_DATA_ANALYSIS_PORT_2); + return; + } + + out_frame_len = data_read(UART_DATA_ANALYSIS_PORT_2, frame, (uint16_t)frame_len); + if (out_frame_len != frame_len) + { + return; + } + + // 校验CRC_16 + uint16_t crc_16 = 0; + uint16_t crc16 = crc16_compute(&frame[0], out_frame_len - 2); + osel_memcpy(&crc[0], &frame[out_frame_len - 2], 2); + crc_16 = BUILD_UINT16(crc[1], crc[0]); + if (crc16 != crc_16) + { + LOG_PRINT("crc error crc16:%x, crc_16:%x\n"); + return; + } + + out_frame_len -= 2; // 去掉CRC_16 + + LOG_PRINT("data_analysis_event2 ok:"); + LOG_HEX(frame, out_frame_len); +} +/** + * @brief 需要识别帧头和帧尾的数据协议 + * @return {*} + * @note + */ +static void data_register1(void) +{ +/** + * 帧头 帧长度 源地址 目标地址 报文类型 报文体 校验 帧尾 + 1 2 2 2 1 n 2 1 +*/ +#define FRAME_HEAD 0x05 // 帧头 +#define FRAME_TAIL 0x1b // 帧尾 + + // 注册数据解析 + data_reg_t reg; + reg.sd.valid = true; // 数据头部验证有效标志位 + reg.sd.len = 1; // 数据头部长度 + reg.sd.pos = 0; // 数据头部偏移量 + reg.sd.data[0] = FRAME_HEAD; // 数据头部数据 + reg.ld.len = 2; // 数据长度 + reg.ld.pos = 2; // 报文长度包含帧长,这里需要设置偏移2 + reg.ld.valid = true; // 数据长度有效标志位 + reg.ld.little_endian = false; // 数据长度是否小端模式 + reg.argu.len_max = UART_RXSIZE; // 数据最大长度 + reg.argu.len_min = 2; // 数据最小长度 + reg.ed.valid = true; // 数据尾部有效标志位 + reg.ed.len = 1; // 数据尾部长度 + reg.ed.data[0] = FRAME_TAIL; // 数据尾部数据 + reg.echo_en = false; // 是否回显 + reg.func_ptr = data_analysis_event1; // 数据解析回调函数 data_analysis模块处理完数据后,会调用这个函数继续数据协议的处理 + uart_data_analysis_cb = data_fsm_init(UART_DATA_ANALYSIS_PORT_1); // 注册数据处理函数 data_analysis模块会调用这个函数,将数据写入到data_analysis模块 + data_reg(UART_DATA_ANALYSIS_PORT_1, reg); // 注册数据解析 +} + +/** + * @brief 需要识别帧头和没有帧尾的数据协议 + * @return {*} + * @note + */ +static void data_register2(void) +{ +/** + * 帧头 帧长度 源地址 目标地址 报文类型 报文体 校验 + 2 2 2 2 1 n 2 +*/ +#define FRAME_HEAD1 0xD5 // 帧头 +#define FRAME_HEAD2 0xC8 // 帧尾 + + // 注册数据解析 + data_reg_t reg; + reg.sd.valid = true; // 数据头部验证有效标志位 + reg.sd.len = 2; // 数据头部长度 + reg.sd.pos = 0; // 数据头部偏移量 + reg.sd.data[0] = FRAME_HEAD1; // 数据头部数据 + reg.sd.data[1] = FRAME_HEAD2; // 数据头部数据 + reg.ld.len = 2; // 数据长度 + reg.ld.pos = 2; // 报文长度包含帧长,这里需要设置偏移2 + reg.ld.valid = true; // 数据长度有效标志位 + reg.ld.little_endian = false; // 数据长度是否小端模式 + reg.argu.len_max = UART_RXSIZE; // 数据最大长度 + reg.argu.len_min = 2; // 数据最小长度 + reg.ed.valid = false; // 数据尾部有效标志位 + reg.echo_en = false; // 是否回显 + reg.func_ptr = data_analysis_event2; // 数据解析回调函数 data_analysis模块处理完数据后,会调用这个函数继续数据协议的处理 + uart_data_analysis_cb = data_fsm_init(UART_DATA_ANALYSIS_PORT_2); // 注册数据处理函数 data_analysis模块会调用这个函数,将数据写入到data_analysis模块 + data_reg(UART_DATA_ANALYSIS_PORT_2, reg); // 注册数据解析 +} + +int32_t main(void) +{ + data_register1(); + data_register2(); + + // 模拟串口数据 + uint8_t data1[] = {0x05, 0x00, 0x0a, 0xff, 0xff, 0x00, 0x01, 0x00, 0x55, 0x40, 0x1b}; + for (uint16_t i = 0; i < ARRAY_LEN(data1); i++) + { + uart_data_analysis_cb(UART_DATA_ANALYSIS_PORT_1, *(data1 + i)); + } + + // 模拟串口数据 + uint8_t data2[] = {0xD5, 0xC8, 0x00, 0x07, 0xff, 0xff, 0x00, 0x01, 0x00, 0x55, 0x40}; + for (uint16_t i = 0; i < ARRAY_LEN(data2); i++) + { + uart_data_analysis_cb(UART_DATA_ANALYSIS_PORT_2, *(data2 + i)); + } + + return 0; +} diff --git a/lib/examples/simple_sqqueue.c b/lib/examples/simple_sqqueue.c new file mode 100644 index 0000000..3ed8c91 --- /dev/null +++ b/lib/examples/simple_sqqueue.c @@ -0,0 +1,53 @@ +#include "../inc/data_type_def.h" +#include "../inc/log.h" +#include "../inc/osel_arch.h" +#include "../inc/sqqueue.h" + +typedef struct +{ + uint8_t x; + uint8_t y; +} element_t; + +sqqueue_ctrl_t queue; // 创建队列对象 + +void traverse_cb(const void *e) +{ + element_t *p = (element_t *)e; + LOG_PRINT("x = %d, y = %d", p->x, p->y); +} + +int32_t main(void) +{ + int size = 10; + // 初始化队列 + if (FALSE == sqqueue_ctrl_init(&queue, sizeof(element_t), size)) + { + LOG_ERR("queue init failed!"); + return -1; // 创建失败 + } + + // 添加测试元素 + for (int i = 1; i <= 10; i++) + { + element_t element; + element.x = i * 10; + element.y = i * 10; + queue.enter(&queue, &element); // 将成员插入到队列中 + } + LOG_PRINT("add queue len = %d", queue.get_len(&queue)); // 获取队列长度 + + queue.del(&queue); // 移除首元素 + LOG_PRINT("del queue len = %d", queue.get_len(&queue)); // 获取队列长度 + queue.revoke(&queue); // 移除尾元素 + LOG_PRINT("revoke queue len = %d", queue.get_len(&queue)); // 获取队列长度 + queue.remove(&queue, 3); // 删除相对队头指定偏移位置的元素 + LOG_PRINT("remove queue len = %d", queue.get_len(&queue)); // 获取队列长度 + + LOG_PRINT("queue traverse:"); + queue.traverse(&queue, traverse_cb); // 遍历队列 + + queue.clear_sqq(&queue); // 清空队列 + LOG_PRINT("clear queue len = %d", queue.get_len(&queue)); // 获取队列长度 + return 0; +} diff --git a/lib/flow/.vscode/c_cpp_properties.json b/lib/flow/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ff92585 --- /dev/null +++ b/lib/flow/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "C:/TDM-GCC-64/bin/gcc.exe", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/lib/flow/.vscode/launch.json b/lib/flow/.vscode/launch.json new file mode 100644 index 0000000..da1e2e9 --- /dev/null +++ b/lib/flow/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "e:/work/stm32/epm/User/lib/flow", + "program": "e:/work/stm32/epm/User/lib/flow/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/lib/flow/.vscode/settings.json b/lib/flow/.vscode/settings.json new file mode 100644 index 0000000..104d25b --- /dev/null +++ b/lib/flow/.vscode/settings.json @@ -0,0 +1,58 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false +} \ No newline at end of file diff --git a/lib/flow/README.md b/lib/flow/README.md new file mode 100644 index 0000000..f9fbfd2 --- /dev/null +++ b/lib/flow/README.md @@ -0,0 +1,244 @@ +# flow_lib + +#### 介绍 +适用于嵌入式单片机的裸机程序微库,只占用你的rom 6个字节,是的,6个字节。颠覆式的设计思维,让你写代码的时候像flow(流水)一样丝滑,让你永远不用在为delay时cpu空转而烦恼,附加的超轻便的软件定时器让你轻松实现各种定时需求,另还有信号量的配方,让你任务间的同步像诗一样写意,并且能让你裸机程序效率提升百倍以上。 + +#### 移植说明 +移植特别简单,flow_def.h有一个全局变量: +``` +extern unsigned long flow_tick; +``` + +把这个变量放在你的某个硬件中断里去,这个硬件中断一定要是一直运行的,推荐RTC半秒中断,或者systick中断都可以。 + +然后在flow.h里的第一行有个宏 +``` +#define FL_HARD_TICK (500) /* 系统硬件中断一次所需要的时间,单位ms */ +``` + +把这里的值改成你的硬件中断一次所需的时间,单位是毫秒,比如你的flow_tick放在了一个500ms中断一次的rtc里,那么这里的宏FL_HARD_TICK的值就是500,具体中断设为多少取决于你的系统最短一次的延时的时间。 + +假如我的最短延时需求是100ms,那么我就得给个100ms中断一次的硬件中断源,宏FL_HARD_TICK的值就是100,我就可以这样使用: +``` +FL_LOCK_DELAY(fl, FL_CLOCK_SEC /10); +``` +来延时100ms。 + +#### 使用说明 +核心文件时flow.h,看这里的注释基本就会使用大部分功能。 + +``` +#ifndef __FLOW_ +#define __FLOW_ + +#include +#include +#include + +#define FL_HARD_TICK (500) /* 系统硬件中断一次所需要的时间,单位ms */ +#define FL_CLOCK_SEC (1000/FL_HARD_TICK) /* 一秒钟需要的tick,可以除也可以自行添加其它宏 */ + +/** + * 初始化一个flow进程 + */ +#define FL_INIT(fl) FLOW_INIT(fl) + +/** + * flow头,必须放在函数内的最前面 + */ +#define FL_HEAD(fl) FLOW_HEAD(fl) + +/** + * flow尾,必须放在函数内的最后面 + */ +#define FL_TAIL(fl) FLOW_TAIL(fl) + +/** + * 给进程加锁,直到judge为真,加锁期间一直放开cpu给其他进程使用 + */ +#define FL_LOCK_WAIT(fl, judge) FLOW_LOCK_WAIT(fl, judge) + +/** + * 如果judge为真,就一直给进程加锁,加锁期间一直放开cpu给其他进程使用 + */ +#define FL_LOCK_WHILE(fl, judge) FLOW_LOCK_WHILE(fl, judge) + +/** + * 退出该进程 + */ +#define FL_EXIT(fl) FLOW_EXIT(fl) + +/** + * 无条件锁住进程一次,下次进来再接着往下运行 + */ +#define FL_LOCK_ONCE(fl) FLOW_LOCK_ONCE(fl) + +/** + * 等待一个flow进程结束 + */ +#define FL_WAIT_PROCESS_END(fl, process) FLOW_WAIT_PROCESS_END(fl, process) + +/** + * 等待一个flow子进程结束 + */ +#define FL_WAIT_CHILD(fl, cfl, process) FLOW_WAIT_CHILD_PROCESS_END(fl, cfl, process) + +/** + * 给进程加锁,时长为time,加锁期间一直放开cpu给其他进程使用,time如果用FL_CLOCK_SEC来乘,那么time的单位就是s + * 此处time必须是常数 + */ +#define FL_LOCK_DELAY(fl,time) FLOW_LOCK_DELAY(fl,time) + +/** + * 给进程加锁,时长为time,延时期间如果judge为真,就直接解锁进程 + * 此处time必须是常数 + */ +#define FL_LOCK_DELAY_OR_WAIT(fl,judge,time) FLOW_LOCK_DELAY_OR_WAIT(fl,judge,time) + +/** + * 初始化一个信号量 + */ +#define FL_SEM_INIT(sem, count) FLOW_SEM_INIT(sem, count) + +/** + * 给进程加锁,直到有信号释放 + */ +#define FL_LOCK_WAIT_SEM(f, sem) FLOW_LOCK_WAIT_SEM(f, sem) + +/** + * 给进程加锁,直到有信号或者超时,此处time可以为变量,其他的接口处time必须是常数 + */ +#define FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time) FLOW_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time) + +/** + * 释放一个信号量 + */ +#define FL_SEM_RELEASE(sem) FLOW_SEM_RELEASE(sem) + +/** + * 初始化一个软件定时器 + */ +void fl_timer_set(struct flow_timer *t, unsigned long interval); + +/** + * 复位一个软件定时器 + */ +void fl_timer_reset(struct flow_timer *t); + +/** + * 重启一个软件定时器 + */ +void fl_timer_restart(struct flow_timer *t); + +/** + * 检测一个软件定时器是否超时,0为不超时,1为超时 + */ +char fl_timer_timeout(struct flow_timer *t); + +/** + * 检测一个软件定时器还剩多少时间超时,单位为硬件tick,比如硬件tick 500ms中断一次,那么 + * 返回的时间单位就是500ms + */ +unsigned long fl_hour_much_time(struct flow_timer *t); + +#endif /* __FLOW_ */ +``` + + +简单举个例子,先从需求说起,假如说你现在需要一个函数,这个函数的功能是每隔1s让你的led亮一次,正常设计的要么起个软件定时器或者硬件定时器,甚至状态机可以实现需求,但是都太low了,让我们看一下如何用flow库来实现这个函数。 + +该函数格式如下: + +``` +char led_flash(struct flow *fl) +{} +``` +其中char、struct flow *fl是必备的。 + +再来看看函数里面的内容格式: + +``` +char led_flash(struct flow *fl) +{ + FL_HEAD(fl); + FL_TAIL(fl); +} +``` +函数里面的FL_HEAD和FL_TAIL是使用flow库的所必须的宏,FL_HEAD(fl)放到函数的最前面,如果你的函数内部有变量定义的话放在变量定义的后面。而FL_TAIL(fl)是放在函数最后面一行的。 + +基本格式有了,再来看下如何实现延时一秒呢?其实只用一个语句就OK。 + +``` +char led_flash(struct flow *fl) +{ + FL_HEAD(fl); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_open(); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_close(); + FL_TAIL(fl); +} +``` + +是的,你没看错,仅仅只需要FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1)这一个语句就OK,当执行到这个语句的时候该函数就会让出CPU权限,当延时时间到了之后,就会回来接着执行FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1)下面的语句。一直到FL_TAIL(fl),该函数就会结束任务,再也不会执行了,那么如果我们想让它一直循环执行呢?看下面: + +``` +char led_flash(struct flow *fl) +{ + FL_HEAD(fl); + while(1) + { + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_open(); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_close(); + } + FL_TAIL(fl); +} +``` +看起来像不像个进程?其实也有点操作系统的样子了。。。 + +光有这个函数也不行,还得进行一些额外的操作 + +比如: + +``` +static struct flow fl_led; /* 1,定义一个struct flow变量给这个函数使用 */ + +static char led_flash(struct flow *fl) +{ + FL_HEAD(fl); + led_init(); /* 这里还能解决你的初始化问题,这里的函数只会在开机时或者说进程第一次进来时运行一次,以后将永远不会运行。注意:如果放在 + FL_HEAD(fl)前面,那么就是每次轮到这个进程运行的时侯就会运行一次,总之很灵活 */ + while(1) + { + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_open(); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); + led_close(); + } + FL_TAIL(fl); +} + +int main(void) +{ + FL_INIT(&fl_led); /* 2,初始化struct flow变量 */ + while(1) + { + led_flash(&fl_led); /* 3,把led_flash进程放在main函数的while循环里 */ + ... + } + return 0; +} +``` +经过以上3步,就可以实现进程之间的切换啦。然后想根据某个条件来锁住线程释放CPU的话,可以把里面的 +``` +FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1); +``` +换成 +``` +FL_LOCK_WAIT(fl, judge); +``` +当里面的judge为假时线程就一直锁住在这一行语句,当judge为真时就可以往下执行啦。同理可以完成很多其他的神奇功能,让你的cpu再也不空转啦,具体请看flow.h文件。。。。 + +这个版本暂时先写这么多,先看看example.c。 diff --git a/lib/flow/example.c b/lib/flow/example.c new file mode 100644 index 0000000..ef0ecc4 --- /dev/null +++ b/lib/flow/example.c @@ -0,0 +1,28 @@ +#include "flow.h" + +/* 1,初始化一个struct flow变量 */ +static struct flow fl_led; + +static char led_flash(struct flow *fl) +{ + FL_HEAD(fl); + for (;;) + { + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1U); /* 延时一秒 */ + led_open(); + FL_LOCK_DELAY(fl, FL_CLOCK_SEC * 1U); /* 延时一秒 */ + led_close(); + } + FL_TAIL(fl); +} + +int main(void) +{ + FL_INIT(&fl_led); + for (;;) + { + led_flash(&fl_led); + // other_process(); + } + return 0; +} diff --git a/lib/flow/flow.h b/lib/flow/flow.h new file mode 100644 index 0000000..30e6401 --- /dev/null +++ b/lib/flow/flow.h @@ -0,0 +1,129 @@ +/** + * @file flow.h + * @author: xxx + * @date: 2023-07-21 17:00:15 + * @brief + * @copyright: Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __FLOW_ +#define __FLOW_ + +#include "flow_def.h" +#include "flow_core.h" +#include "flow_sem.h" + +#define FL_HARD_TICK (10U) /* 系统硬件中断一次所需要的时间,单位ms */ +#define FL_CLOCK_SEC (1000U / FL_HARD_TICK) /* 一秒钟需要的tick,可以根据需求添加其他时间更短的宏 */ +#define FL_CLOCK_100MSEC (100U / FL_HARD_TICK) +#define FL_CLOCK_10MSEC (FL_CLOCK_100MSEC / 10U) + +/** + * 初始化一个flow进程 + */ +#define FL_INIT(fl) FLOW_INIT((fl)) + +/** + * flow头,必须放在函数内的最前面 + */ +#define FL_HEAD(fl) FLOW_HEAD((fl)) + +/** + * flow尾,必须放在函数内的最后面 + */ +#define FL_TAIL(fl) FLOW_TAIL((fl)) + +/** + * 给进程加锁,直到judge为真,加锁期间一直放开cpu给其他进程使用 + */ +#define FL_LOCK_WAIT(fl, judge) FLOW_LOCK_WAIT((fl), (judge)) + +/** + * 如果judge为真,就一直给进程加锁,加锁期间一直放开cpu给其他进程使用 + */ +#define FL_LOCK_WHILE(fl, judge) FLOW_LOCK_WHILE((fl), (judge)) + +/** + * 退出该进程 + */ +#define FL_EXIT(fl) FLOW_EXIT((fl)) + +/** + * 无条件锁住进程一次,下次进来再接着往下运行 + */ +#define FL_LOCK_ONCE(fl) FLOW_LOCK_ONCE((fl)) + +/** + * 等待一个flow进程结束 + */ +#define FL_WAIT_PROCESS_END(fl, process) FLOW_WAIT_PROCESS_END((fl), (process)) + +/** + * 等待一个flow子进程结束 + */ +#define FL_WAIT_CHILD(fl, cfl, process) FLOW_WAIT_CHILD_PROCESS_END((fl), (cfl), (process)) + +/** + * 给进程加锁,时长为time,加锁期间一直放开cpu给其他进程使用,time如果用FL_CLOCK_SEC来乘,那么time的单位就是s + * 此处time必须是常数 + */ +#define FL_LOCK_DELAY(fl, time) FLOW_LOCK_DELAY((fl), (time)) + +/** + * 给进程加锁,时长为time,延时期间如果judge为真,就直接解锁进程 + * 此处time必须是常数 + */ +#define FL_LOCK_DELAY_OR_WAIT(fl, judge, time) FLOW_LOCK_DELAY_OR_WAIT((fl), (judge), (time)) + +/** + * 初始化一个信号量 + */ +#define FL_SEM_INIT(sem, count) FLOW_SEM_INIT((sem), (count)) + +/** + * 给进程加锁,直到有信号释放 + */ +#define FL_LOCK_WAIT_SEM(fl, sem) FLOW_LOCK_WAIT_SEM((fl), (sem)) + +/** + * 给进程加锁,直到有信号或者超时,此处time可以为常数或者变量,其他的接口处time必须是常数 + */ +#define FL_LOCK_WAIT_SEM_OR_TIMEOUT(fl, sem, time) FLOW_LOCK_WAIT_SEM_OR_TIMEOUT((fl), (sem), (time)) + +/** + * 释放一个信号量 + */ +#define FL_SEM_RELEASE(sem) FLOW_SEM_RELEASE((sem)) + +/** + * 检测一个信号量是否被释放 + */ +#define FL_SEM_IS_RELEASE(fl, sem) FLOW_SEM_IS_RELEASE((fl), (sem)) + +/** + * 初始化一个软件定时器 + */ +void fl_timer_set(struct flow_timer *t, unsigned long interval); + +/** + * 复位一个软件定时器 + */ +void fl_timer_reset(struct flow_timer *t); + +/** + * 重启一个软件定时器 + */ +void fl_timer_restart(struct flow_timer *t); + +/** + * 检测一个软件定时器是否超时,0为不超时,1为超时 + */ +unsigned char fl_timer_timeout(struct flow_timer *t); + +/** + * 检测一个软件定时器还剩多少时间超时,单位为硬件tick,比如硬件tick 500ms中断一次,那么 + * 返回的剩余时间就是500ms*n + */ +unsigned long fl_hour_much_time(struct flow_timer *t); + +#endif /* __FLOW_ */ diff --git a/lib/flow/flow_core.c b/lib/flow/flow_core.c new file mode 100644 index 0000000..87bb5bd --- /dev/null +++ b/lib/flow/flow_core.c @@ -0,0 +1,83 @@ +/** + * @file + * @author xxx + * @date 2023-07-21 17:00:15 + * @brief + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "flow.h" + +unsigned long flow_tick; + +/** + * @brief 设置流量计器的间隔时间。 + * @param t 要设置的流量计器。 + * @param interval 要设置的间隔时间值。 + * @return 如果间隔时间无效或无法设置,则返回NULL。 + * @note 此函数设置间隔时间并重置计时器,因此它将在下一个间隔时间 occurs时开始。 + */ +void fl_timer_set(struct flow_timer *t, unsigned long interval) +{ + if (interval == 0) + { + // 如果间隔时间为零,返回错误 + return; + } + + t->interval = interval; + t->start = flow_tick; +} + +/** + * @brief 重置流量计器。 + * @param t 要重置的流量计器。 + * @return 如果无法重置计时器,则返回NULL。 + * @note 此函数将计时器的开始时间加上间隔时间,因此它将在下一个间隔时间 occurs时开始。 + */ +void fl_timer_reset(struct flow_timer *t) +{ + t->start += t->interval; +} + +/** + * @brief 重新启动流量计器。 + * @param t 要重新启动的流量计器。 + * @return 如果无法重新启动计时器,则返回NULL。 + * @note 此函数将计时器的开始时间设置为当前flow_tick,因此它将在重新开始时从基本开始。 + */ +void fl_timer_restart(struct flow_timer *t) +{ + t->start = flow_tick; +} + +/** + * @brief 检查给定的flow_timer结构体的超时状态 + * @param {flow_timer} *t 指向flow_timer结构体的指针 + * @return {unsigned char} 超时返回1,否则返回0 + * @note 检查当前时间与flow_timer结构体中的start时间之差是否大于或等于interval + */ +unsigned char fl_timer_timeout(struct flow_timer *t) +{ + return ((flow_tick - t->start) >= t->interval) ? 1U : 0U; +} + +/** + * @brief 计算给定flow_timer结构体中的时间长度 + * @param {flow_timer} *t 指向flow_timer结构体的指针 + * @return {unsigned long} 返回时间长度 + * @note 计算start时间加上interval与当前时间flow_tick之差,如果大于等于flow_tick,则返回time_len - flow_tick,否则返回0 + */ +unsigned long fl_hour_much_time(struct flow_timer *t) +{ + unsigned long time_len = t->start + t->interval; + + if (time_len >= flow_tick) + { + return (time_len - flow_tick); + } + else + { + return 0U; + } +} diff --git a/lib/flow/flow_core.h b/lib/flow/flow_core.h new file mode 100644 index 0000000..b6857a3 --- /dev/null +++ b/lib/flow/flow_core.h @@ -0,0 +1,106 @@ +/** + * @file flow_core.h + * @author: xxx + * @date: 2023-07-21 17:00:15 + * @brief + * @copyright: Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __FLOW_CORE_ +#define __FLOW_CORE_ + +#include "flow_def.h" + +// 在定时器中断中调用 +#define FLOW_TICK_UPDATE() \ + do \ + { \ + flow_tick++; \ + } while (0); + +// 初始化一个flow进程 +#define FLOW_INIT(f) ((f)->line = 0) + +// flow头,必须放在函数内的最前面 +#define FLOW_HEAD(f) \ + { \ + volatile char lock_once_flag = 0; \ + switch ((f)->line) \ + { \ + case 0: +// flow尾,必须放在函数内的最后面 +#define FLOW_TAIL(f) \ + } \ + ; \ + lock_once_flag = (f)->line = 0; \ + return FLOW_END; \ + } \ + ; + +// 给进程加锁,直到judge为真,加锁期间一直放开cpu给其他进程使用 +#define FLOW_LOCK_WAIT(f, judge) \ + do \ + { \ + (f)->line = __LINE__; \ + case __LINE__:; \ + if (!(judge)) \ + return FLOW_WAIT; \ + } while (0) + +// 如果judge为真,就一直给进程加锁,加锁期间一直放开cpu给其他进程使用 +#define FLOW_LOCK_WHILE(f, judge) \ + do \ + { \ + (f)->line = __LINE__; \ + case __LINE__:; \ + if (judge) \ + return FLOW_WAIT; \ + } while (0) + +// 退出该进程 +#define FLOW_EXIT(f) \ + do \ + { \ + (f)->line = 0; \ + return FLOW_FINISH; \ + } while (0) + +// 无条件锁住进程一次,下次进来再接着往下运行 +#define FLOW_LOCK_ONCE(f) \ + do \ + { \ + lock_once_flag = 1; \ + (f)->line = __LINE__; \ + case __LINE__:; \ + if (lock_once_flag) \ + return FLOW_LOCK; \ + } while (0) + +// 等待一个flow进程结束 +#define FLOW_WAIT_PROCESS_END(f, process) FLOW_LOCK_WHILE(f, (process) < FLOW_FINISH) + +// 等待一个flow子进程结束 +#define FLOW_WAIT_CHILD_PROCESS_END(f, cf, process) \ + do \ + { \ + FLOW_INIT((cf)); \ + FLOW_WAIT_PROCESS_END((f), (process)); \ + } while (0) + +// 给进程加锁,时长为time,加锁期间一直放开cpu给其他进程使用,time如果用FL_CLOCK_SEC来乘,那么time的单位就是s +#define FLOW_LOCK_DELAY(f, t) \ + do \ + { \ + (f)->time = flow_tick; \ + FLOW_LOCK_WAIT((f), ((flow_tick - (f)->time) >= (t))); \ + } while (0) + +// 给进程加锁,时长为time,延时期间如果judge为真,就直接解锁进程 +#define FLOW_LOCK_DELAY_OR_WAIT(f, judge, t) \ + do \ + { \ + (f)->time = flow_tick; \ + FLOW_LOCK_WAIT((f), ((judge) || ((flow_tick - (f)->time) >= (t)))); \ + } while (0) + +#endif /* __FLOW_CORE_ */ diff --git a/lib/flow/flow_def.h b/lib/flow/flow_def.h new file mode 100644 index 0000000..1537877 --- /dev/null +++ b/lib/flow/flow_def.h @@ -0,0 +1,37 @@ +/** + * @file flow_def.h + * @author: xxx + * @date: 2023-07-21 17:00:15 + * @brief + * @copyright: Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __FLOW_DEF_ +#define __FLOW_DEF_ + +#define FLOW_WAIT (0) +#define FLOW_LOCK (1) +#define FLOW_FINISH (2) +#define FLOW_END (3) + +struct flow +{ + unsigned long line; + unsigned long time; +}; + +struct flow_timer +{ + unsigned long start; + unsigned long interval; +}; + +struct flow_sem +{ + unsigned long count; + unsigned long time; +}; + +extern unsigned long flow_tick; + +#endif /* __FLOW_DEF_ */ diff --git a/lib/flow/flow_sem.h b/lib/flow/flow_sem.h new file mode 100644 index 0000000..b524683 --- /dev/null +++ b/lib/flow/flow_sem.h @@ -0,0 +1,40 @@ +/*** + * @file: + * @author: xxx + * @date: 2023-07-21 17:00:15 + * @brief + * @copyright: Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __FLOW_SEM_H__ +#define __FLOW_SEM_H__ + +#include "flow_def.h" +#include "flow_core.h" + +#define FLOW_SEM_INIT(s, c) ((s)->count = c) // 初始化信号量s的计数值为c + +// 等待信号量s的计数值大于0 +#define FLOW_LOCK_WAIT_SEM(f, s) \ + do \ + { \ + FLOW_LOCK_WAIT(f, (s)->count > 0); \ + --(s)->count; \ + } while (0) + +// 等待信号量s的计数值大于0,或者当前时间与锁f的时间之差大于等于t +#define FLOW_LOCK_WAIT_SEM_OR_TIMEOUT(f, s, t) \ + do \ + { \ + (f)->time = flow_tick; \ + (s)->time = (t); \ + FLOW_LOCK_WAIT(f, (((s)->count > 0) || ((flow_tick - (f)->time) >= ((s)->time)))); \ + if (((s)->count > 0) && ((flow_tick - (f)->time) < ((s)->time))) \ + --(s)->count; \ + } while (0) + +#define FLOW_SEM_RELEASE(s) (++(s)->count) + +#define FLOW_SEM_IS_RELEASE(f, s) (flow_tick - (f)->time) < ((s)->time) + +#endif /* __FLOW_SEM_H__ */ diff --git a/lib/inc/aes.h b/lib/inc/aes.h new file mode 100644 index 0000000..7c2a92c --- /dev/null +++ b/lib/inc/aes.h @@ -0,0 +1,161 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#if 1 +#define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 1 +#define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 0 +#define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +#define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 0 +#define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 0 +#define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef uint8_t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint8_t length_type; + +typedef struct +{ + uint8_t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint8_t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED) + +return_type aes_set_key(const uint8_t key[], + length_type keylen, + aes_context ctx[1]); +#endif + +#if defined(AES_ENC_PREKEYED) + +return_type aes_encrypt(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1]); + +return_type aes_cbc_encrypt(const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1]); +#endif + +#if defined(AES_DEC_PREKEYED) + +return_type aes_decrypt(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const aes_context ctx[1]); + +return_type aes_cbc_decrypt(const uint8_t *in, + uint8_t *out, + int32_t n_block, + uint8_t iv[N_BLOCK], + const aes_context ctx[1]); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined(AES_ENC_128_OTFK) +void aes_encrypt_128(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK]); +#endif + +#if defined(AES_DEC_128_OTFK) +void aes_decrypt_128(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], + uint8_t o_key[N_BLOCK]); +#endif + +#if defined(AES_ENC_256_OTFK) +void aes_encrypt_256(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK]); +#endif + +#if defined(AES_DEC_256_OTFK) +void aes_decrypt_256(const uint8_t in[N_BLOCK], + uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], + uint8_t o_key[2 * N_BLOCK]); +#endif + +#endif diff --git a/lib/inc/clist.h b/lib/inc/clist.h new file mode 100644 index 0000000..d04158a --- /dev/null +++ b/lib/inc/clist.h @@ -0,0 +1,49 @@ +/** + * @file clist.h + * @author xxx + * @date 2023-08-08 23:18:15 + * @brief 简单链表 使用方法 lib\examples\simple_clist.c + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __CLIST_H +#define __CLIST_H +#include "lib.h" +typedef void *cnode; + +/// 链表中一个节点的结构体 +typedef struct CLIST_NODE +{ + cnode data; /// 值 + struct CLIST_NODE *Next; /// 指向下一个结点 +} clist_node_t; + +void clist_init(clist_node_t **ppFirst); ///< 初始化 ,构造一条空的链表 + +void clist_print(clist_node_t *First); ///< 打印链表 + +uint32_t clist_node_count(clist_node_t *First); ///< 获取链表节点数 + +void clist_push_back(clist_node_t **ppFirst, cnode data); ///< 尾部插入 + +void clist_push_front(clist_node_t **ppFirst, cnode data); ///< 头部插入 + +void clist_pop_back(clist_node_t **ppFirst); ///< 尾部删除 + +void clist_pop_front(clist_node_t **ppFirst); ///< 头部删除 + +void clist_insert_for_node(clist_node_t **ppFirst, clist_node_t *pPos, cnode data); ///< 给定结点插入,插入到结点前 + +int32_t clist_insert(clist_node_t **ppFirst, int32_t Pos, cnode data); ///< 按位置插入 + +void clist_erase_for_node(clist_node_t **ppFirst, clist_node_t *pPos); ///< 给定结点删除 + +void clist_remove(clist_node_t **ppFirst, cnode data); ///< 按值删除,只删遇到的第一个 + +void clist_remove_all(clist_node_t **ppFirst, cnode data); ///< 按值删除,删除所有的 + +void clist_destroy(clist_node_t **ppFirst); ///< 销毁 ,需要销毁每一个节点 + +clist_node_t *clist_find(clist_node_t *pFirst, cnode data); ///< 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL + +#endif //__CLIST_H diff --git a/lib/inc/cmac.h b/lib/inc/cmac.h new file mode 100644 index 0000000..7ff5585 --- /dev/null +++ b/lib/inc/cmac.h @@ -0,0 +1,63 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ + +#ifndef _CMAC_H_ +#define _CMAC_H_ + +#include "aes.h" + +#define AES_CMAC_KEY_LENGTH 16 +#define AES_CMAC_DIGEST_LENGTH 16 + +typedef struct _AES_CMAC_CTX +{ + aes_context rijndael; + uint8_t X[16]; + uint8_t M_last[16]; + uint32_t M_n; +} AES_CMAC_CTX; + +// #include + +//__BEGIN_DECLS +void AES_CMAC_Init(AES_CMAC_CTX *ctx); +void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]); +void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len); +// __attribute__((__bounded__(__string__,2,3))); +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx); +// __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH))); +//__END_DECLS + +#endif /* _CMAC_H_ */ diff --git a/lib/inc/cmd.h b/lib/inc/cmd.h new file mode 100644 index 0000000..735776a --- /dev/null +++ b/lib/inc/cmd.h @@ -0,0 +1,49 @@ +/** + * @file cmd.h + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 命令解析器 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef _CMD_H_ +#define _CMD_H_ + +#define CMD_HASH 0xb433e5c6 + +#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM Compiler */ +#define SECTION(x) __attribute__((section(x))) +#define CMD_USED __attribute__((used)) + +#elif defined(__IAR_SYSTEMS_ICC__) /* IAR Compiler */ +#define SECTION(x) @x +#define CMD_USED __root +#else +#error "not supported tool chain..." +#endif + +typedef void (*cmd_handler)(void); + +typedef struct cmd +{ + const char *cmd; + const char *cmd_mess; + unsigned int hash; + cmd_handler handler; +} cmd_t; + +/// 注册命令 +#define REGISTER_CMD(cmd, handler, desc) \ + const char _register_##cmd##_cmd[] = #cmd; \ + const char _register_##cmd##_desc[] = #desc; \ + CMD_USED cmd_t _register_##cmd SECTION("CMDS") = \ + { \ + _register_##cmd##_cmd, \ + _register_##cmd##_desc, \ + (unsigned int)CMD_HASH, \ + (cmd_handler)&handler}; + +void cmd_init(void); ///< 初始化命令 +void cmd_parsing(char *str); ///< 命令解析 + +#endif diff --git a/lib/inc/data_analysis.h b/lib/inc/data_analysis.h new file mode 100644 index 0000000..4ac52fe --- /dev/null +++ b/lib/inc/data_analysis.h @@ -0,0 +1,79 @@ +/** + * @file data_analysis.h + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 处理传输层的数据 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_ +#define COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_ +#include "data_type_def.h" + +typedef enum +{ + DATA_1, + DATA_2, + DATA_MAX, +} DataId_t; // 处理数据模块的个数,请根据实际情况修改 + +#define DATA_NUM (DATA_MAX) + +#define DATA_BUF_RECV_SQQ_LEN 650u +#define DATA_BUF_SEND_SQQ_LEN 0u + +#define DATA_SD_LEN_MAX 2 +#define DATA_LD_LEN_MAX 2 +#define DATA_ED_LEN_MAX 1 + +typedef struct _data_reg_t_ +{ + struct + { + uint8_t len; + uint8_t pos; + uint8_t data[DATA_SD_LEN_MAX]; + BOOL valid; // 是否有效 + } sd; // start delimiter + + struct + { + uint8_t len; + uint8_t pos; // 偏移量,在wait_end_state中根据帧长去掉固定长度来判断是否是结束符 + uint8_t little_endian; + BOOL valid; // 是否有效 + } ld; // length describe + + struct + { + uint16_t len_max; + uint16_t len_min; + } argu; + + struct + { + uint8_t len; + uint8_t data[DATA_ED_LEN_MAX]; + BOOL valid; + } ed; + + BOOL echo_en; + void (*func_ptr)(void); +} data_reg_t; + +typedef void (*data_interupt_cb_t)(uint8_t id, uint8_t ch); ///< 中断回调函数,数据从这里写入 + +extern uint8_t data_read(uint8_t id, void *buffer, uint16_t len); ///< 读取数据 + +extern void data_write(uint8_t id, uint8_t *const string, uint16_t len); ///< TODO 写入数据 + +extern void lock_data(uint8_t data_id); ///< 锁定数据,防止中断写入数据 + +extern void unlock_data(uint8_t data_id); ///< 解锁数据 + +extern data_interupt_cb_t data_fsm_init(uint8_t data_id); ///< 初始化数据状态机 + +extern BOOL data_reg(uint8_t id, data_reg_t reg); ///< 注册数据 + +extern void data_unreg(uint8_t id); ///< 注销数据 +#endif /* COMPONENTS_COMMON_INCLUDE_DATA_ANALYSIS_H_ */ diff --git a/lib/inc/data_type_def.h b/lib/inc/data_type_def.h new file mode 100644 index 0000000..90de350 --- /dev/null +++ b/lib/inc/data_type_def.h @@ -0,0 +1,290 @@ +/*** + * @Author: + * @Date: 2023-03-29 13:16:28 + * @LastEditors: xxx + * @LastEditTime: 2023-03-30 00:34:11 + * @Description:数据类型定义 + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __DATA_TYPE_DEF_H_ +#define __DATA_TYPE_DEF_H_ +#include +#ifndef PI +#define PI (3.14159265358979323846f) +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifndef OK +typedef enum +{ + OK = 0, + FAIL = !OK, +} state_e; +#endif + +#ifndef __IO +#define __IO volatile +#endif + +typedef unsigned char BOOL; /* boolean data */ +typedef unsigned char bool_t; /* boolean data */ + +#if !defined(__stdint_h) && !defined(_GCC_WRAP_STDINT_H) +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned long int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed long int int32_t; +typedef long long int64_t; +#endif + +typedef float float32; +typedef double float64; + +#ifndef float32_t +typedef float float32_t; +#endif + +#ifndef float64_t +typedef double float64_t; +#endif + +#pragma pack(1) +typedef struct +{ + uint8_t bs[3]; +} uint24_t; +typedef struct +{ + uint8_t bs[5]; +} uint40_t; + +typedef union +{ + float32 f; + int32_t c; +} float32_u; + +#pragma pack() + +typedef enum +{ + DATA_TYPE_INT8 = 0, // 8-bit signed integer + DATA_TYPE_UINT8, // 8-bit unsigned integer + DATA_TYPE_INT16, // 16-bit signed integer + DATA_TYPE_UINT16, // 16-bit unsigned integer + DATA_TYPE_INT32, // 32-bit signed integer + DATA_TYPE_UINT32, // 32-bit unsigned integer + DATA_TYPE_INT64, // 64-bit signed integer + DATA_TYPE_UINT64, // 64-bit unsigned integer + DATA_TYPE_FLOAT, // 32-bit floating point number + DATA_TYPE_DOUBLE, // 64-bit floating point number + DATA_TYPE_STRING, // string + DATA_TYPE_ARRAY, // array + DATA_TYPE_STRUCT, // structure + DATA_TYPE_UNION, // union + DATA_TYPE_ENUM, // enumeration + DATA_TYPE_POINTER, // pointer + DATA_TYPE_FUNCTION, // function + DATA_TYPE_VOID, // void + DATA_TYPE_MAX, +} data_type_e; + +typedef uint16_t nwk_id_t; + +/** + * STANDARD BITS + */ +#ifndef BIT0 +#define BIT0 (0x01u) +#define BIT1 (0x02u) +#define BIT2 (0x04u) +#define BIT3 (0x08u) +#define BIT4 (0x10u) +#define BIT5 (0x20u) +#define BIT6 (0x40u) +#define BIT7 (0x80u) +#define BIT8 (0x0100u) +#define BIT9 (0x0200u) +#define BIT10 (0x0400u) +#define BIT11 (0x0800u) +#define BIT12 (0x1000u) +#define BIT13 (0x2000u) +#define BIT14 (0x4000u) +#define BIT15 (0x8000u) +#define BIT16 (0x00010000u) +#define BIT17 (0x00020000u) +#define BIT18 (0x00040000u) +#define BIT19 (0x00080000u) +#define BIT20 (0x00100000u) +#define BIT21 (0x00200000u) +#define BIT22 (0x00400000u) +#define BIT23 (0x00800000u) +#define BIT24 (0x01000000u) +#define BIT25 (0x02000000u) +#define BIT26 (0x04000000u) +#define BIT27 (0x08000000u) +#define BIT28 (0x10000000u) +#define BIT29 (0x20000000u) +#define BIT30 (0x40000000u) +#define BIT31 (0x80000000u) + +#define BIT_SET(x, b) x |= b // 置位 +#define BIT_CLR(x, b) x &= ~b // 清零 +#define BIT_GET(x, y) ((x) >> (y) & 1) // 获取某一位 +#define BIT_REVERSE(x, y) (x) ^= (1 << y) // 某位取反 +#define BIT_IS_SET(x, b) ((x) & (b)) // 判断某一位是否为1 +#define BIT_IS_CLR(x, b) (!((x) & (b))) // 判断某一位是否为0 + +#endif + +#ifndef BF +/** + * @brief 从一个字节中提取指定位的值 + * @return {*} + * @note + *> uint8_t num = 0x12; 二进制表示为00010010

+ *> uint8_t bit = 2; 提取第2位(从0开始计数)

+ *> uint8_t width = 1; 提取1位

+ *> uint8_t result = BF(num, bit, width); 结果为1

+ */ +#define BF(x, b, s) (((x) & (b)) >> (s)) +#endif + +#ifndef MIN +/** + * @brief + * @return {*} + * @note + *> int num1 = 10;

+ *> int num2 = 20;

+ *> int result = MIN(num1, num2); // 结果为10

+ */ +#define MIN(n, m) (((n) < (m)) ? (n) : (m)) +#endif + +#ifndef MAX +/** + * @brief + * @return {*} + * @note + *> int num1 = 10;

+ *> int num2 = 20;

+ *> int result = MAX(num1, num2); // 结果为20

+ */ +#define MAX(n, m) (((n) < (m)) ? (m) : (n)) +#endif + +#ifndef ABS +/** + * @brief + * @return {*} + * @note + *> int num = -10; + *> int result = ABS(num); // 结果为10 + */ +#define ABS(n) (((n) < 0) ? -(n) : (n)) +#endif + +#ifndef RANGE +#define RANGE(x, a, b) (MIN(MAX(x, a), b)) +#endif + +/** + * @brief Macro to check if a value is between a minimum and maximum value (inclusive). + * @param x The value to check. + * @param min The minimum value. + * @param max The maximum value. + * @return Returns 1 if the value is between the minimum and maximum values (inclusive), 0 otherwise. + */ +#define IS_BETWEEN(x, min, max) ((x) >= min && (x) <= max) + +#define ARRAY_LEN(arr) (sizeof(arr)) / (sizeof(arr[0])) + +#define HI_UINT16(a) (((uint16_t)(a) >> 8) & 0xFF) +#define LO_UINT16(a) ((uint16_t)(a) & 0xFF) + +#define HI_1_UINT32(a) (((uint32_t)(a) >> 24) & 0xFF) +#define HI_2_UINT32(a) (((uint32_t)(a) >> 16) & 0xFF) +#define HI_3_UINT32(a) (((uint32_t)(a) >> 8) & 0xFF) +#define HI_4_UINT32(a) ((uint32_t)(a) & 0xFF) + +#define LO_1_UINT8(a) (uint8_t)((a) & 0xFF) +#define LO_2_UINT8(a) (uint8_t)(((a) & 0xFF00) >> 8) +#define LO_3_UINT8(a) (uint8_t)(((a) & 0xFF0000) >> 16) +#define LO_4_UINT8(a) (uint8_t)(((a) & 0xFF000000) >> 24) + +// uint32小端转大端 +#define S2B_UINT32(a) \ + (((uint32_t)(a) & 0xFF000000) >> 24) + (((uint32_t)(a) & 0x00FF0000) >> 8) + (((uint32_t)(a) & 0x0000FF00) << 8) + (((uint32_t)(a) & 0x000000FF) << 24) + +// uint32大端转小端 +#define B2S_UINT32(a) S2B_UINT32(a) + +// uint16小端转大端 +#define S2B_UINT16(a) ((((uint16_t)(a) & 0xFF00) >> 8) + (((uint16_t)(a) & 0x00FF) << 8)) + +// uint16大端转小端 +#define B2S_UINT16(a) S2B_UINT16(a) + +#define BUILD_UINT16(loByte, hiByte) \ + ((uint16_t)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8))) + +// float32小端转大端 +static inline float32 S2B_FLOAT32(float fv) +{ + float32_u _f; + _f.f = fv; + _f.c = S2B_UINT32(_f.c); + return _f.f; +} + +// float32大端转小端 +#define B2S_FLOAT32(a) S2B_FLOAT32(a) + +// 反序数组 +#define REVERSE_ARRAY(arr, len) \ + do \ + { \ + uint8_t _tmp; \ + uint16_t _i; \ + for (_i = 0; _i < len / 2; _i++) \ + { \ + _tmp = arr[_i]; \ + arr[_i] = arr[len - _i - 1]; \ + arr[len - _i - 1] = _tmp; \ + } \ + } while (0); + +// 比较2个数组是否相等 +#define IsEqual(arr1, arr2, n) ({ \ + int _equal = 1; \ + for (int _i = 0; _i < n; _i++) \ + { \ + if (arr1[_i] != arr2[_i]) \ + { \ + _equal = 0; \ + break; \ + } \ + } \ + _equal; \ +}) + +// ASSIC码转换为数字 +#define ASCII_TO_NUM(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : (c) - 'A' + 10) + +// 数字转换为ASSIC码 +#define NUM_TO_ASCII(x) ((x) < 10 ? (x) + '0' : (x) - 10 + 'A') + +#define FLOAT_TO_UINT16(x) (x * 8192 / 100 + 2048) ///> 浮点压缩uint16_t +#define UINT16_TO_FLOAT(x) (100 * (x - 2048) / 8192) ///> uint16转浮点 +#endif /* __DATA_TYPE_DEF_H_ */ diff --git a/lib/inc/debug.h b/lib/inc/debug.h new file mode 100644 index 0000000..e1745f0 --- /dev/null +++ b/lib/inc/debug.h @@ -0,0 +1,21 @@ +/*** + * @Author: + * @Date: 2023-04-04 08:13:11 + * @LastEditors: xxx + * @LastEditTime: 2023-04-04 13:21:46 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#ifndef __DEBUG_H +#define __DEBUG_H +#include "lib.h" + +/*形参*/ +#define _DBG_LINE_ , uint16_t line +/*实参*/ +#define __DBG_LINE , __LINE__ + +extern BOOL DBG_ASSERT(uint8_t cond _DBG_LINE_); + +#endif //__DEBUG_H diff --git a/lib/inc/filter.h b/lib/inc/filter.h new file mode 100644 index 0000000..de165d9 --- /dev/null +++ b/lib/inc/filter.h @@ -0,0 +1,53 @@ +/** + * @file filter.h + * @author xxx + * @date 2023-08-08 22:59:46 + * @brief + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __FILTER_H__ +#define __FILTER_H__ +#include "lib.h" + +typedef struct +{ + float32 x; // 卡尔曼滤波器的估计值 + float32 a; // 状态转移矩阵(1,表示没有动态变化) + float32 h; // 观测矩阵(1,表示直接观测) + float32 q; // 过程噪声协方差 + float32 r; // 观测噪声协方差 + float32 p; // 估计误差协方差 + float32 gain; // 卡尔曼增益 + + float32 change_max; // 允许的最大变化量,用于判断观测值是否异常 +} kalman_t; // 卡尔曼滤波器结构 + +typedef struct +{ + BOOL fisrt_flag; // 第一次标志位 + float32 alpha; // 滤波系数 0~1 + float32 last_value; // 上次滤波结果 +} lpf_t; // 一阶低通滤波器 + +typedef struct +{ + uint16_t size; // 滑动窗口大小 + float32 *window; // 滑动窗口 + volatile float32 sum; // 滑动窗口和 + volatile float32 out; // 滤波结果 + uint16_t index; // 滑动窗口索引 +} lpf_window_t; // 滑动窗口滤波器 + +void kalman_init(kalman_t *cfg, float32 change_max); +float32 kalman_update(kalman_t *cfg, float32 input); + +void lpf_init(lpf_t *cfg); +float32 lpf_update(lpf_t *cfg, float32 input); +void lpf_reset(lpf_t *cfg); + +void lpf_window_init(lpf_window_t *cfg, uint16_t size); +void lpf_window_dinit(lpf_window_t *cfg); +float32 lpf_window_update(lpf_window_t *cfg, float32 input); +void lpf_window_reset(lpf_window_t *cfg); +#endif // __FILTER_H__ diff --git a/lib/inc/fsm.h b/lib/inc/fsm.h new file mode 100644 index 0000000..4bdb7b6 --- /dev/null +++ b/lib/inc/fsm.h @@ -0,0 +1,307 @@ +#ifndef __FSM_H__ +#define __FSM_H__ +#include +/* ----------------------- Defines ------------------------------------------*/ +// 用于快速识别出 STATE与STEP +#define FSM_STATE(name) state_##name +#define FSM_FUNCT(name) funct_##name + +// 数据类型定义区 +typedef signed char state; +typedef long long step_ret; +typedef void *AS_STEP_RETVAL; + +/*! + * @brief 状态机过程实现原型函数 + * 设计状态机时需要按照这个模式写 + * + * @param[in] void* 你所需要的任何参数 + * + * @return 返回值 代表下一个状态 + */ +typedef void *(*Procedure)(void *); + +typedef struct +{ + state ds; // 默认状态 + state cs; // 当前状态 + state ns; // 下个状态 +} SM_STATE; + +// 状态机 属性 定义 +typedef struct +{ + // 状态管理 + SM_STATE st; + + // 状态机跳转表 + Procedure *procedures; + + // 状态机数据区域 + void *data; + + // 错误处理(用于存放 状态 执行 的结果) + step_ret ret_ptr; // 状态 执行结果 + void *err_ptr; + state err_flag; +} FSM; + +/* ----------------------- Start function declaration -----------------------------*/ + +/*! + * @brief 设置状态机的错误容器 + * + * @param[in] fsm 状态机实例 + * + * @param[in] err_var 容器 + * + * @return 是/否 + */ +static inline void set_err_var(FSM *fsm, void *err_var) +{ + if (!fsm) + return; + fsm->err_ptr = err_var; +} + +/*! + * @brief 获取错误值容器(用于读取其中的内容) + * + * @param[in] fsm 状态机实例 + * + * @return 是/否 + */ +static inline void *get_err_var(FSM *fsm) +{ + return fsm->err_ptr; +} + +/*! + * @brief 获取状态机 在 步进中是否遇到了错误。 + * + * @param[in] fsm 状态机实例 + * + * @return 是/否 + */ +static inline state is_fsm_error(FSM *fsm) +{ + return fsm->err_flag; +} + +/*! + * @brief 置 状态机 错误位 + * + * @param[in] fsm 状态机实例 + * + * @return 是/否 + */ +static inline state set_fsm_error_flag(FSM *fsm) +{ + if (!fsm) + return -1; + fsm->err_flag = 1; + return 0; +} + +/*! + * @brief 置 状态机 错误位 + * + * @param[in] fsm 状态机实例 + * + * @return 是/否 + */ +static inline state clr_fsm_error_flag(FSM *fsm) +{ + if (!fsm) + return -1; + fsm->err_flag = 0; + return 0; +} + +/*! + * @brief 为状态机添加 过程方法 序列 + * + * @param[in] fsm 状态机实例 + * + * @param[in] procedures 状态机的所有过程方法 + * + */ +static inline void set_procedures(FSM *fsm, Procedure *procedures) +{ + if (fsm) + { + fsm->procedures = procedures; + fsm->st.cs = -1; // 执行run之前,当前状态是未定的 + } +} + +/*! + * @brief 配置状态机的数据域 + * + * @param[in] fsm 状态机实例 + * + * @param[in] data 状态机需要的数据域 + * + */ +static inline void set_data_entry(FSM *fsm, void *data) +{ + if (fsm) + fsm->data = data; +} + +/*! + * @brief 配置状态机的数据域 + * + * @param[in] fsm 状态机实例 + * + * @return 返回 状态机 数据域 + * + */ +static inline void *get_data_entry(FSM *fsm) +{ + return fsm->data; +} + +/*! + * @brief 让 状态机 步进一次 + * + * @param[in] fsm 状态机实例 + * + * @return 非负数 :代表 所成功执行的状态 + * -1 : 失败 + */ +static inline state run_state_machine_once(FSM *fsm) +{ + if (!fsm) + return -1; + + // 切换到新状态 + fsm->st.cs = fsm->st.ns; + + // 跳转到下一个状态(状态 执行 结果 保存在 ret_ptr 中 ) + fsm->ret_ptr = (step_ret)fsm->procedures[fsm->st.cs](fsm); + + return fsm->st.cs; +} + +/*! + * @brief 获取步进执行结果 + * + * @param[in] fsm 状态机实例 + * + * @return 是/否 + */ +static inline step_ret *get_step_retval(FSM *fsm) +{ + return &fsm->ret_ptr; +} + +/*! + * @brief 获取状态机的当前状态 + * + * @param[in] fsm 状态机实例 + * + * @return 当前状态 + */ +static inline state get_curr_state(FSM *fsm) +{ + return fsm->st.cs; +} + +/*! + * @brief 设置状态机默认状态 + * + * @param[in] fsm 状态机实例 + * + * @param[in] st 状态值 + * + */ +static inline void set_default_state(FSM *fsm, state st) +{ + if (!fsm) + return; + fsm->st.ds = st; + fsm->st.ns = st; +} + +/*! + * @brief 设置状态机的下次状态 + * + * @param[in] fsm 状态机实例 + * + * @param[in] st 状态值 + * + */ +static inline void set_next_state(FSM *fsm, state st) +{ + if (fsm) + fsm->st.ns = st; +} + +/*! + * @brief 获取状态机的下次状态 + * + * @param[in] fsm 状态机实例 + * + * @return 下一个状态 + */ +static inline state get_next_state(FSM *fsm) +{ + return fsm->st.ns; +} + +/*! + * @brief 将状态机设为默认状态 + * + * @param[in] fsm 状态机实例 + * + */ +static inline void init_state_machine(FSM *p) +{ + set_next_state(p, p->st.ds); + p->st.cs = -1; // 执行run之前,当前状态是未定的 +} + +/*! + * @brief 将状态机设为默认状态,同时清除错误状态 + * + * @param[in] fsm 状态机实例 + * + */ +static inline void reset_state_machine(FSM *p) +{ + if (!p) + return; + clr_fsm_error_flag(p); + init_state_machine(p); +} + +/*! + * @brief 判断状态机是否在某个状态 + * + * @param[in] fsm 状态机实例 + * + * @param[in] st 状态值 + * + * @return 是/否 + */ +static inline state is_curr_state(FSM *fsm, state st) +{ + return fsm->st.cs == st; +} + +/*! + * @brief 判断状态机是否即将进行某个状态 + * + * @param[in] fsm 状态机实例 + * + * @param[in] st 状态值 + * + * @return 是/否 + */ +static inline state is_next_state(FSM *fsm, state st) +{ + return fsm->st.ns == st; +} + +#endif // __FSM_H__ diff --git a/lib/inc/lib.h b/lib/inc/lib.h new file mode 100644 index 0000000..aba352a --- /dev/null +++ b/lib/inc/lib.h @@ -0,0 +1,127 @@ +/*** + * @Author: + * @Date: 2023-04-04 08:13:11 + * @LastEditors: xxx + * @LastEditTime: 2023-04-04 10:13:21 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __LIB_H +#define __LIB_H +#include +#include +#include +#include "data_type_def.h" +#include "malloc.h" +#include "data_analysis.h" +#include "osel_arch.h" +#include "debug.h" +#include "sqqueue.h" +#include "clist.h" + +#define INTERNAL_EXTERN extern + +#ifndef STM32 +#include "log.h" +#else +#define LOG_PRINT(fmt, ...) \ + do \ + { \ + } while (0); +#define LOG_ERR(fmt, ...) \ + do \ + { \ + } while (0); +#define LOG_HEX(data, len) \ + do \ + { \ + } while (0); + +#endif + +#define EXIT(x) \ + do \ + { \ + DBG_ASSERT(FALSE, __DBG_LINE); \ + } while (0); + +////< 时间结构 +typedef union +{ + uint8_t data[6]; + struct + { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + } date; +} date_time_t; + +typedef struct +{ + uint16_t year; + uint8_t month; + uint8_t day; +} rtc_date_t; + +typedef struct +{ + uint8_t hour; + uint8_t minute; + uint8_t second; +} rtc_time_t; + +typedef struct +{ + float32 x; + float32 y; +} point_t; + +typedef struct +{ + float32 a; + float32 b; +} linear_func_param_t; + +extern void assic_to_str(uint8_t *assic, uint8_t len, uint8_t *str); // ASCII码转字符串 +extern void get_cpu_id(uint32_t *id); // 获取CPU ID +extern uint32_t cpu_encrypt(void); // CPU加密 +extern BOOL cpu_judge_encrypt(uint32_t cupid_encrypt); // CPU判断加密 +extern void version_split(uint8_t *version, uint8_t *hi, uint8_t *lo); // 版本号1.0拆解成1和0 +extern void reverse(uint8_t *buf, uint16_t len); // 反序数组 +extern BOOL is_in_array(uint16_t *arr, uint16_t len, uint16_t val); // 判断val是否在数组arr中 +extern BOOL is_same_value(uint8_t *buf, uint16_t len, uint8_t value); // 判断数组中的值是否都相等 +extern uint16_t crc16_compute(const uint8_t *const data, uint16_t length); // CRC16校验 +extern uint32_t crc32_compute(const uint8_t *const data, uint16_t length); // CRC32校验 +extern uint64_t crc64_compute(const uint8_t *const data, const uint16_t length); // CRC64校验 +extern uint8_t xor_compute(const uint8_t *const data, uint16_t length); // 异或校验 +extern uint8_t get_bit_num(uint8_t bit); // 获取bit位的值 +extern BOOL is_bit_set(int x, int k); // 判断x的第k位是否为1 +extern uint8_t is_leap_year(uint16_t year); // 检查是否是闰年 +extern BOOL is_valid_date(uint8_t year, uint8_t month, uint8_t day); // 检查日期是否有效 +extern BOOL is_valid_time(uint8_t hour, uint8_t minute, uint8_t second); // 检查时间是否有效 +extern BOOL is_valid_datetime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); // 检查日期和时间是否有效 +extern uint32_t days_since_1970(uint16_t year, uint8_t month, uint8_t day); // 计算从1970年1月1日到给定年月日的天数 +extern uint32_t date_to_seconds(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); // 将日期转换为秒数 +extern void seconds_to_date(uint32_t total_seconds, rtc_date_t *date, rtc_time_t *time); // 将秒数转换为日期 +extern uint16_t dayOfyear(uint16_t year, uint8_t month, uint8_t day); // 计算一年中的第几天 +extern uint16_t weekOfyear(uint16_t year, uint8_t month, uint8_t day); // 计算一年中的第几周 +extern uint8_t get_weekday(uint16_t year, uint8_t month, uint8_t day); // 获取今天星期几 +extern uint8_t hex_format_dec(uint8_t hex); // 十六进制转十进制 +extern uint8_t dec_format_hex(uint8_t dec); // 十进制转十六进制 +extern void quicksort(uint16_t arr[], int low, int high); // 快速排序 +extern void insertion_sort(uint16_t arr[], uint16_t n); // 插入排序 + +extern uint32_t time2stamp(const rtc_date_t *const date, const rtc_time_t *const time); // 日期时间转时间戳 +extern void stamp2time(uint32_t stamp, rtc_date_t *date, rtc_time_t *time); // 时间戳转北京时间 +extern void convert_seconds(uint32_t total_seconds, char *date); // 秒数转换成时分秒 +extern void add_commas(uint32_t num, char *result); // 数字添加逗号 + +extern linear_func_param_t calculate_linear_regression(const point_t *points, int32_t count); // 计算线性回归 +extern float32 get_linearity_value(const point_t *points, int32_t count, linear_func_param_t param); // 获取线性度 +#endif //__LIB_H diff --git a/lib/inc/log.h b/lib/inc/log.h new file mode 100644 index 0000000..802ba9f --- /dev/null +++ b/lib/inc/log.h @@ -0,0 +1,45 @@ +/*** + * @Author: + * @Date: 2023-03-20 19:27:47 + * @LastEditors: xxx + * @LastEditTime: 2023-03-30 00:34:41 + * @Description:日志打印模块PC端调试使用 + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __LOG_H_ +#define __LOG_H_ +#include +#include + +#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__) + +/*调试日志宏定义*/ + +#define LOG_PRINT(fmt, ...) \ + do \ + { \ + printf("[DEBUG:%s][%s:%d] " fmt "\n", __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0); + +/*错误日志打印(在日志打印模块还未启动时使用)*/ +#define LOG_ERR(fmt, ...) \ + do \ + { \ + printf("[ERROR:%s][%s:%d] " fmt "\n", __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0); + +// 打印十六进制字符串 +#define LOG_HEX(data, len) \ + do \ + { \ + printf("[DEBUG:%s][%s:%d] ", __FILENAME__, __FUNCTION__, __LINE__); \ + for (int i = 0; i < len; i++) \ + { \ + printf("%02x ", data[i]); \ + } \ + printf("\n"); \ + } while (0); + +#endif //__LOG_H_ diff --git a/lib/inc/malloc.h b/lib/inc/malloc.h new file mode 100644 index 0000000..5fc5468 --- /dev/null +++ b/lib/inc/malloc.h @@ -0,0 +1,45 @@ +#ifndef _MOLLOC_H +#define _MOLLOC_H + +#include "../inc/data_type_def.h" + +#ifndef NULL +#define NULL 0 +#endif + +// 定义两个内存池 +#define SRAMIN 0 // 内部内存池 +#define SRAMEX 1 // 外部内存池(精英STM32开发板不支持外部内存) +// 我们又多少个SRAM可管理 +#define SRAMBANK 2 // 定义支持的SRAM块数. 精英版实际上只支持1个内存区域,即内部内存. + +// mem1内存参数设定.mem1完全处于内部SRAM里面.(设置内部SARM的内存池和内存表的参数) +#define MEM1_BLOCK_SIZE 8 // 一个内存块大小为32字节 +#define MEM1_MAX_SIZE 8 * 1024 // 最大管理内存 1K (我们这个内存管理系统的内部SRAM可控制的内存大小) +#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE / MEM1_BLOCK_SIZE // 内存表大小(有多少块内存块) + +// mem2内存参数设定.mem2的内存池处于外部SRAM里面 +#define MEM2_BLOCK_SIZE 8 // 一个内存块大小为32字节 +#define MEM2_MAX_SIZE 0 * 1024 // 因为精英版没有外扩内存,故这里设置一个最小值 +#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE / MEM2_BLOCK_SIZE // 内存表大小 + +// 内存管理控制器结构体 +// 注意:内存管理由内存池和内存列表组成 +// SRAMBANK:SARM块数,一般有内部SRAM和外部SRAM、CCM +struct _m_mallco_dev +{ + void (*init)(uint8_t); // 初始化 + uint8_t (*perused)(uint8_t); // 内存使用率 + uint8_t *membase[SRAMBANK]; // 内存池 管理SRAMBANK个区域的内存 + uint16_t *memmap[SRAMBANK]; // 内存管理状态表 + uint8_t memrdy[SRAMBANK]; // 内存管理是否就绪 +}; + +void my_mem_init(uint8_t memx); +uint8_t my_mem_perused(uint8_t memx); + +void *mymalloc(uint8_t memx, uint32_t size); +void myfree(uint8_t memx, void *ptr); +void *myrealloc(uint8_t memx, void *ptr, uint32_t size); + +#endif diff --git a/lib/inc/mlist.h b/lib/inc/mlist.h new file mode 100644 index 0000000..ebc043f --- /dev/null +++ b/lib/inc/mlist.h @@ -0,0 +1,268 @@ +/*** + * @Author: + * @Date: 2023-04-04 08:39:32 + * @LastEditors: xxx + * @LastEditTime: 2023-04-04 08:46:20 + * @Description:双向链表操作接口 该双向链表的操作,请参照Linux内核 (include/linux/list.h) + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __LIST_H +#define __LIST_H +#include +#include "data_type_def.h" +typedef struct list_head +{ + struct list_head *next; + struct list_head *prev; +} list_head_t; + +/** + * 获得链表元素所在实体的地址, 该实体在链表中保存 + * + * @param ptr: 链表的入口指针 + * @param type: 结构类型 + * @param member: 元素结构中链表变量的名字 + * + * @return 指向该元素所在实体的指针 + */ +#define list_entry_addr_find(ptr, type, member) \ + ((type *)((char *)(ptr) - (char *)(&((type *)NULL)->member))) + +/** + * 移除并返回链表中首元素所在的实体,该实体在链表中不保存 + * + * @param head: 链表的入口指针 + * @param type: 结构类型 + * @param member: 指向该链表的第一个元素的指针 + * + * @return 表首元素所在实体的指针,链表为空则返回空指针 + */ +#define list_entry_decap(head, type, member) \ + ( \ + (list_empty(head)) ? (type *)NULL \ + : (list_entry_addr_find(list_next_elem_get(head), type, member))) + +#define list_entry_get_head(head, type, member) \ + ((list_empty(head)) ? (type *)NULL : (list_entry_addr_find((head)->next, type, member))) +/** + * 移除并返回链表尾部所在实体,该实体在链表中不保存 + * + * @param head: 链表入口指针 + * @param type: 链表所在结构体的名称 + * @param member: 结构体中,链表变量的名称 + * + * @return 表尾部所在实体指针,链表为空则返回空指针 + */ +#define list_entry_curtail(head, type, member) \ + ((list_empty(head)) ? (type *)NULL \ + : (list_entry_addr_find(list_curtail(head), type, member))) + +/** + * 正向遍历链表,在该遍历中不能对链表做删除操作 -- list_for_each + * + * @param pos: 链表元素计数器 + * @param head: 链表的入口指针 + */ +#define list_for_each_forwards(pos, head) \ + for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) + +/** + * 反向遍历链表,在该遍历中不能对链表做删除操作 + * + * @param pos: 链表元素计数器 + * @param head: 链表的入口指针 + */ +#define list_for_each_backwards(pos, head) \ + for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) + +/** + * 链表遍历,支持删除操作 + * + * @param pos: 链表元素计数器 + * @param n: 临时链表元素 + * @param head: 链表入口指针 + */ +#define list_for_each_safe(pos, n, head) \ + for ((pos) = (head)->next, n = (pos)->next; (pos) != (head); \ + (pos) = n, n = (pos)->next) + +/** + * 遍历链表所在实体,不可删除实体 + * + * @param pos: 链表元素计数器 + * @param head: 链表入口指针 + * @param type: 链表所在结构体的名称 + * @param member: 结构体中,链表变量的名称 + */ +#define list_entry_for_each(pos, head, type, member) \ + for ((pos) = list_entry_addr_find((head)->next, type, member); \ + &(pos)->member != (head); \ + (pos) = list_entry_addr_find((pos)->member.next, type, member)) + +/** + * 遍历链表所在实体, 支持删除操作 + * + * @param pos: 链表元素计数器 + * @param n: 临时链表元素 + * @param head: 链表入口指针 + * @param type: 链表所在结构体的名称 + * @param member: 结构体中,链表变量的名称 + */ +#define list_entry_for_each_safe(pos, n, head, type, member) \ + for ((pos) = list_entry_addr_find((head)->next, type, member), \ + n = list_entry_addr_find((pos)->member.next, type, member); \ + &(pos)->member != (head); \ + (pos) = n, n = list_entry_addr_find(n->member.next, type, member)) + +/** + * 计算链表中元素的个数 + */ +#define list_count(head, count) \ + do \ + { \ + count = 0; \ + for (list_head_t *pos = (head)->next; pos != (head); pos = pos->next) \ + { \ + count++; \ + } \ + } while (0) + +/** + * 将实体按顺序插入到链表中合适的地方,顺序由函数funcCompare来决定 -- list_sorted_add + * + * @param new_entry: 所要插入的实体 + * @param head: 链表头 + * @param type: 链表所在结构体的名称 + * @param member: 结构体中,链表变量的名称 + * @param func_compare: 顺序比较函数,声明:bool func_compare(Node* A, Node* B) + * 如果 A < B, 返回 true, 否则返回 false + * @param pos: 链表元素计数器 + */ +#define list_entry_sorted_add(new_entry, head, type, member, func_compare, pos) \ + do \ + { \ + type *entry_a = NULL; \ + type *entry_b = NULL; \ + for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) \ + { \ + entry_a = list_entry_addr_find((new_entry), type, member); \ + entry_b = list_entry_addr_find((pos), type, member); \ + if (func_compare(entry_a, entry_b)) \ + { \ + break; \ + } \ + } \ + if ((pos) != (head)) \ + { \ + list_insert_forwards((new_entry), (pos)); \ + } \ + else \ + { \ + list_add_to_tail((new_entry), (head)); \ + } \ + } while (__LINE__ == -1) + +/** + * 链表头初始化 + * + * @param ptr: 需要被初始化的链表头指针 + */ +void list_init(list_head_t *const ptr); + +/** + * 在指定位置之前插入新的元素 + * + * @param new_entry: 需要放入链表中的新元素 + * @param pos: 链表中放入新元素的位置指针 + */ +void list_insert_forwards(list_head_t *const new_entry, list_head_t *const pos); + +/** + * 在指定位置之后插入新的元素 + * + * @param new_entry: 需要放入链表中的新元素 + * @param pos: 链表中放入新元素的位置指针 + */ +void list_insert_backwards(list_head_t *const new_entry, list_head_t *const pos); + +/** + * 在链表尾部之后插入新的元素 -- list_append + * + * @param new_entry: 需要放入链表尾部的新元素 + * @param list: 链表头指针 + */ +void list_add_to_tail(list_head_t *const new_entry, list_head_t *const list); + +/** + * 在链表头部之后插入新的元素 + * + * @param new_entry: 需要放入链表尾部的新元素 + * @param list: 链表头指针 + */ +void list_add_to_head(list_head_t *const new_entry, list_head_t *const list); + +/** + * 将指定元素从链表中删除 + * + * @param elem: 需要删除的链表元素 + */ +void list_del(list_head_t *const elem); + +/** + * 将链表的尾元素删除并返回 + * + * @param head: 链表指针 + * + * @return 链表尾元素 + */ +list_head_t *list_curtail(const list_head_t *const head); + +/** + * 判断链表是否为空 + * + * @param head: 链表头指针 + * + * @return 为空时返回TRUE,否则为FALSE + */ +bool list_empty(const list_head_t *const head); + +/** + * 获取链表中第一个元素的地址 -- list_get_head + * + * @param head: 链表头指针 + * + * @return 首元素的地址 + */ +list_head_t *list_first_elem_look(const list_head_t *const head); + +/** + * 取出给定位置的下一个元素 + * + * @param pos: 链表元素地址 + * + * @return 下一个链表元素地址 + */ +list_head_t *list_next_elem_get(const list_head_t *const pos); + +/** + * 将一个元素从一个链表中移除,然后再插入另外一个链表中的头部 + * + * @param elem: 被移除的链表元素 + * @param head: 新链表头 + */ +void list_move_to_another_head(list_head_t *const elem, list_head_t *const head); + +/** + * 将一个元素从一个链表中移除,然后再放入另外一个链表中的尾部 + * + * @param elem: 被移除的链表元素 + * @param head: 新链表头 + */ +void list_move_to_another_tail(list_head_t *const elem, list_head_t *const head); + +#endif +/** + * @} + */ diff --git a/lib/inc/osel_arch.h b/lib/inc/osel_arch.h new file mode 100644 index 0000000..d310e23 --- /dev/null +++ b/lib/inc/osel_arch.h @@ -0,0 +1,181 @@ +/*** + * @Author: + * @Date: 2023-04-04 08:13:11 + * @LastEditors: xxx + * @LastEditTime: 2023-04-04 08:16:58 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __OSEL_ARCH_H__ +#define __OSEL_ARCH_H__ + +#include "lib.h" +#define hal_int_state_t char +#ifdef STM32 +#include "main.h" +#define HAL_ENTER_CRITICAL(__HANDLE__) \ + do \ + { \ + if ((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + } while (0U) + +#define HAL_EXIT_CRITICAL(__HANDLE__) \ + do \ + { \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + } while (0U) +#else +#define HAL_ENTER_CRITICAL(__HANDLE__) + +#define HAL_EXIT_CRITICAL(__HANDLE__) + +#endif + +#define osel_memset _memset +#define osel_memcmp _memcmp +#define osel_memcpy _memcpyL +#define osel_memcpyr _memcpyR +#define osel_reverse _reverse +#define osel_mem_alloc _malloc +#define osel_mem_free _free +#define osel_mem_alloc2 _malloc2 +#define osel_mem_free2 _free2 +#define osel_mstrlen _mstrlen + +static inline void *_malloc(uint32_t size) +{ + return mymalloc(SRAMIN, size); +} + +static inline void _free(void *ptr) +{ + myfree(SRAMIN, ptr); +} + +static inline void *_malloc2(uint32_t size) +{ + return mymalloc(SRAMEX, size); +} + +static inline void _free2(void *ptr) +{ + myfree(SRAMEX, ptr); +} + +/** + * @brief Fills a block of memory with a given value. + * + * @param dst The destination block of memory. + * @param value The value to fill the memory with. + * @param size The size of the memory block, in bytes. + */ +static inline void _memset(uint8_t *dst, uint8_t value, uint16_t size) +{ + while (size--) + { + *dst++ = value; + } +} + +/** + * @brief Compares two blocks of memory for equality. + * + * @param[in] dst The first block of memory to compare. + * @param[in] src The second block of memory to compare. + * @param[in] size The number of bytes to compare. + * + * @return 0 if the blocks of memory are equal, -1 otherwise. + */ +static inline int8_t _memcmp(const uint8_t *dst, const uint8_t *src, uint16_t size) +{ + while (size--) + { + if (*dst++ != *src++) + { + return -1; + } + } + return 0; +} + +/** + * @brief Copies data from a source buffer to a destination buffer in a forward direction. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The number of bytes to copy. + */ +static inline void _memcpyL(uint8_t *dst, const uint8_t *src, uint16_t size) +{ + while (size--) + { + *dst++ = *src++; + } +} + +/** + * @brief Copies data from a source buffer to a destination buffer in reverse order. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The number of bytes to copy. + */ +static inline void _memcpyR(uint8_t *dst, const uint8_t *src, uint16_t size) +{ + // dst is a pointer to the last byte of the destination buffer + // src is a pointer to the first byte of the source buffer + // size is the number of bytes to copy + + // decrement the destination pointer by the size, since we want to write to the last byte of the buffer + dst = dst + (size - 1); + + // loop through each byte in the buffer, copying from the source to the destination in reverse order + while (size--) + { + // write the next byte from the source to the destination + *dst-- = *src++; + } +} + +/** + * @brief Reverses the order of bytes in a buffer + * + * @param buf The buffer to reverse + * @param len The length of the buffer, in bytes + */ +static inline void _reverse(uint8_t *buf, uint16_t len) +{ + uint8_t temp = 0; + uint16_t i; + for (i = 0; i < len / 2; i++) + { + temp = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = temp; + } +} +/** + * @brief Returns the length of a null-terminated string + * + * @param s The string to measure + * @return The length of the string, not including the null terminator + */ +static inline unsigned int _mstrlen(const unsigned char *s) +{ + const unsigned char *ss = s; + while (*ss) + ss++; + + return ss - s; +} + +#endif // __OSEL_ARCH_H__ diff --git a/lib/inc/pbuf.h b/lib/inc/pbuf.h new file mode 100644 index 0000000..ee3a6ce --- /dev/null +++ b/lib/inc/pbuf.h @@ -0,0 +1,169 @@ +/*** + * @Author: + * @Date: 2023-04-04 10:06:40 + * @LastEditors: xxx + * @LastEditTime: 2023-04-04 13:21:27 + * @Description: + * @email: + * @Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef COMPONENTS_COMMON_INCLUDE_PBUF_H_ +#define COMPONENTS_COMMON_INCLUDE_PBUF_H_ +#include "../inc/data_type_def.h" +#include "../inc/mlist.h" +#include "../inc/malloc.h" + +#define PBUF_DBG_EN (1u) +#define PBUF_TYPE_MAX_NUM (3u) + +#define PBUF_NUM_MAX (10u) + +// 如果size>254,并使用data_analysis接收数据,需要修改data_analysis.c中的DATA_BUF_RECV_SQQ_LEN +#define SMALL_PBUF_BUFFER_SIZE (32) +#define MEDIUM_PBUF_BUFFER_SIZE (32 * 2) +#define LARGE_PBUF_BUFFER_SIZE (32 * 4) + +#define SMALL_PBUF_NUM (4u) // 各种PBUF最大个数 +#define MEDIUM_PBUF_NUM (4u) +#define LARGE_PBUF_NUM (4u) + +#if PBUF_DBG_EN > 0 + +/*形参*/ +#define _PLINE1_ , uint16_t line +#define _PLINE2_ , uint16_t line +/*实参*/ +#define __PLINE1 , __LINE__ +#define __PLINE2 , __LINE__ + +#else + +#define _PLINE1_ +#define _PLINE2_ +#define __PLINE1 +#define __PLINE2 + +#endif + +enum _PBUF_TYPE +{ + SMALL_PBUF, + MEDIUM_PBUF, + LARGE_PBUF, + PBUF_TYPE_INVALID +}; + +typedef struct __send_times_t +{ + uint8_t app_send_times; + uint8_t mac_send_times; +} send_times_t; + +typedef struct +{ + int8_t rssi_dbm; + uint8_t seq; + + nwk_id_t src_id; // 接收到数据帧时,为同步模块提供同步对象信息; + nwk_id_t dst_id; // 填写帧的目的节点网络地址 + + uint8_t send_mode : 2, + is_ack : 1, + need_ack : 1, + crc_ok : 1, + is_pending : 1, + debug_info : 1, + reserved : 1; + + send_times_t already_send_times; +} pkt_attri_t; + +typedef struct +{ + struct list_head list; + uint8_t *data_p; // 指向数据区 + uint8_t *head; // 指向数据区的第一个字节 + uint8_t *end; // 指向数据区的最后一个字节 + uint16_t data_len; // 该pbuf的实际数据长度 + pkt_attri_t attri; + bool used; +#if PBUF_DBG_EN > 0 + uint16_t alloc_line; + uint16_t free_line; +#endif +} pbuf_t; + +/** + * pbuf_initz: 为pbuf申请一块内存区域,需要配置各种pbuf的大小和数量等 + */ +void pbuf_initz(void); + +/** + * 申请一个pbuf,用来存放用户数据 + * + * @param size: 用户的数据长度 + * @param _PLINE1_: pbuf_allocz()位置的行号,调用时传入实参形式__PLINE1 + * + * @return: 申请成功则返回pbuf的指针,失败则进入断言 + */ +extern pbuf_t *pbuf_allocz(uint16_t size _PLINE1_); + +/** + * 释放已经使用完的pbuf + * + * @param pbuf: 需要操作的pbuf的指针的指针 + * @param _PLINE2_: 调用pbuf_freez()位置的行号,调用时传入实参形式__PLINE2 + * + * @return: 无 + */ +void pbuf_freez(pbuf_t **const pbuf _PLINE2_); + +/** + * 向pbuf->end方向移动pbuf->data_p指针,移动距离为len + * + * @param pbuf: 需要操作的pbuf的指针 + * @param len: data_p需要移动的距离 + * + * @return: 成功则返回data_p指针,失败返回NULL + */ +extern uint8_t *pbuf_skip_datap_forward(pbuf_t *const pbuf, + uint8_t len); + +/** + * 向pbuf->head方向移动pbuf->data_p指针,移动距离为len + * + * @param pbuf: 需要操作的pbuf的指针 + * @param len: data_p需要移动的距离 + * + * @return: 成功则返回data_p指针,失败返回NULL + */ +extern uint8_t *pbuf_skip_datap_backward(pbuf_t *const pbuf, + uint8_t len); + +/** + * 向pbuf的数据区拷贝数据,并移动data_p指针,改变data_len + * + * @param pbuf: 目的地址pbuf的指针(从pbuf->data_p开始拷贝) + * @param src: 源地址的指针 + * @param len: 需要拷贝的数据长度 + * + * @return: 成功则返回TRUE, 失败则返回FALSE + */ +extern bool pbuf_copy_data_in(pbuf_t *const pbuf, + const uint8_t *const src, + uint8_t len); + +/** + * 从pbuf的数据区拷贝数据,并移动data_p指针,不改变data_len + * + * @param dst: 目的地址的指针 + * @param pbuf: 源地址pbuf的指针(从pbuf->data_p开始拷贝) + * @param len: 需要拷贝的数据长度 + * + * @return: 成功则返回TRUE, 失败则返回 + */ +extern bool pbuf_copy_data_out(uint8_t *const dst, + pbuf_t *const pbuf, + uint8_t len); +#endif /* COMPONENTS_COMMON_INCLUDE_PBUF_H_ */ diff --git a/lib/inc/sqqueue.h b/lib/inc/sqqueue.h new file mode 100644 index 0000000..aac846c --- /dev/null +++ b/lib/inc/sqqueue.h @@ -0,0 +1,52 @@ +/** + * @file sqqueue.h + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 提供循环队列功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#ifndef __SQQUEUE_H +#define __SQQUEUE_H +#include "data_type_def.h" + +typedef struct _sqqueue_t +{ + uint8_t *base; // 队列存储元素的首地址 + uint8_t entry_size; // 队列元素的宽度 + uint16_t sqq_len; // 队列总长 + uint16_t front; // 队列头下标 + uint16_t rear; // 队列尾下标 +} sqqueue_t; + +/** + * 通用循环队列伪类 + * 该队列有九个操作,分别为单元素入队列、多元素入队列、出队列, + * 单元素撤销入队列(队尾删除)、取队列长度、判空、清空队列、遍历和删除指定位置 + */ +typedef struct _sqqueue_ctrl_t +{ + sqqueue_t sqq; + BOOL(*enter) + (struct _sqqueue_ctrl_t *const p_this, const void *const e); // 单元素入队列 + BOOL(*string_enter) + (struct _sqqueue_ctrl_t *const p_this, const void *const string, uint16_t len); // 多元素入队列 + void *(*del)(struct _sqqueue_ctrl_t *const p_this); // 出队列 + void *(*revoke)(struct _sqqueue_ctrl_t *const p_this); // 撤销入队列 + uint16_t (*get_len)(const struct _sqqueue_ctrl_t *const p_this); // 取队列长度 + BOOL(*full) + (const struct _sqqueue_ctrl_t *const p_this); // 判满 + void (*clear_sqq)(struct _sqqueue_ctrl_t *const p_this); // 清空队列 + void (*traverse)(struct _sqqueue_ctrl_t *const p_this, void (*vi)(const void *e)); // 遍历 + void (*remove)(struct _sqqueue_ctrl_t *const p_this, uint16_t location); // 删除指定位置 +} sqqueue_ctrl_t; + +BOOL sqqueue_ctrl_init(sqqueue_ctrl_t *const p_this, + uint8_t entry_size, + uint16_t sqq_len); ///< 初始化 + +void sqqueue_ctrl_dinit(sqqueue_ctrl_t *const p_this); ///< 销毁 +#endif +/** + * @} + */ diff --git a/lib/inc/storage.h b/lib/inc/storage.h new file mode 100644 index 0000000..ca50d28 --- /dev/null +++ b/lib/inc/storage.h @@ -0,0 +1,52 @@ +/** + * @file storage.h + * @author xushenghao + * @date 2024-11-15 15:57:02 + * @brief 一个存储库,不支持平衡擦写 + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +#ifndef __STORAGE_H__ +#define __STORAGE_H__ + +#include "lib.h" + +typedef struct +{ + uint16_t index; + uint16_t size; + uint32_t address; +} storage_node_t; + +typedef struct +{ + clist_node_t *head; + struct + { + uint32_t base_addr; ///< 存储器基地址 + uint16_t page_size; ///< 存储器页大小 + uint16_t variable_count; ///< 存储器变量数量 + uint16_t variable_size; ///< 存储器变量占用大小,如果变量大小不够一页剩余部分则写入下一页,上一页剩余字节计入变量占用大小 + uint16_t page_count; ///< 变量占用存储器页数 + } params; + + struct + { + BOOL(*read) + (uint32_t addr, uint8_t *buf, uint16_t size); + BOOL(*write) + (uint32_t addr, uint8_t * buf, uint16_t size); + BOOL(*erase_page) + (uint32_t page); + } ops; +} storage_t; + +storage_t *storage_init(uint32_t base_addr, uint16_t page_size); ///< 初始化存储器 +void storage_destroy(storage_t *storage); ///< 销毁存储器 +void storage_add_node(storage_t *storage, uint16_t index, uint16_t size); ///< 添加存储节点 +BOOL storage_write(storage_t *storage, uint16_t index, const uint8_t *buf); ///< 存储数据 +BOOL storage_read(storage_t *storage, uint16_t index, uint8_t *buf); ///< 读取数据 +BOOL storage_write_all(storage_t *storage, const uint8_t *buf); ///< 存储所有数据 +BOOL storage_read_all(storage_t *storage, uint8_t *buf); ///< 读取所有数据 +BOOL storage_check(storage_t *storage, uint16_t index, uint8_t *buf); ///< 检查存储数据 +BOOL storage_check_all(storage_t *storage, uint8_t *buf); ///< 检查所有存储数据 +#endif // __STORAGE_H__ diff --git a/lib/inc/wl_flash.h b/lib/inc/wl_flash.h new file mode 100644 index 0000000..694fac9 --- /dev/null +++ b/lib/inc/wl_flash.h @@ -0,0 +1,108 @@ +/** + * @file wl_flash.h + * @author xushenghao + * @date 2024-07-22 13:57:02 + * @brief + * @copyright Copyright (c) 2024 by xxx, All Rights Reserved. + */ +/** + * @brief 磨损平衡算法 + * + * 1. 初始化block_page大小和当前写入地址 + * 2. 写入数据前,检查剩余空间是否足够 + * a. 如果足够,直接写入数据 + * b. 如果不足够,执行以下步骤: + * i. 检查是否有已擦除但未使用的block_page,如果有,移动到该block_page继续写入 + * ii. 如果没有已擦除的block_page,检查当前block_page是否已满 + * - 如果已满,移动到下一个block_page + * - 在移动前,检查该block_page是否有有效数据需要迁移,如果有,则先迁移数据 + * 3. 在决定擦除新的block_page前,采用Wear Leveling算法选择最佳的block_page进行擦除 + * 4. 数据写入后,更新剩余空间大小和当前写入地址 + */ + +/** + * 使用方法 + * + #define LL_FLASH_PAGE_SIZE (2 * 1024) + #define PVD_RESET_STORAGE_START_ADDRESS (256 * LL_FLASH_PAGE_SIZE) + static void test_wl_flash(void) + { + uint32_t address = 0; + wl_flash_t wf = { + .wl_flag = TRUE, + .addr = PVD_RESET_STORAGE_START_ADDRESS, + .len = 2 * LL_FLASH_PAGE_SIZE, + .page_size = LL_FLASH_PAGE_SIZE, + .data_size = sizeof(device_reset_t), + .write_gran = 8, + + .ops.srand = board_srand, + .ops.read = flash_read, + .ops.write = flash_write, + .ops.erase_page = flash_erase_page, + }; + wl_flash_init(&wf); + wl_flash_erase(&wf); + address = wl_flash_get_current_address(&wf); + + device_reset_t power_on; + power_on.flag = PVD_RESET_FLAG; + + // 一页2048/16 = 128个数据,2页 = 256个数据,i=255时数据从头开始 + for (uint16_t i = 0; i < 1000; i++) + { + wl_flash_write(&wf, (uint8_t *)&power_on, sizeof(device_reset_t)); + wl_flash_set_next_address(&wf); + address = wl_flash_get_current_address(&wf); + if (address == wf.addr) + { + __NOP(); + } + } + } + */ + +#ifndef __WL_FLASH_H__ +#define __WL_FLASH_H__ +#include "lib.h" + +typedef struct +{ + BOOL wl_flag; // 开启平衡擦写标志 + uint32_t addr; // Flash 起始地址 + uint32_t len; // Flash 长度 + uint16_t page_size; // Flash 页大小 2^N + uint16_t data_size; // 数据大小 + /* write minimum granularity, unit: byte. + 1(stm32f2/f4)/ 4(stm32f1)/ 8(stm32l4) + 0 will not take effect. */ + uint8_t write_gran; + struct + { + void (*srand)(void); // 随机数种子 + BOOL(*read) + (uint32_t addr, uint8_t *buf, uint16_t size); + BOOL(*write) + (uint32_t addr, const uint8_t *buf, uint16_t size); + BOOL(*erase_page) + (uint32_t page); + } ops; + + struct + { + uint32_t current_address; // 当前准备写入的地址 + uint16_t residue_size; // 剩余可写大小 + uint16_t start_page; // 起始页 + uint16_t page_total; // 总页数 + uint16_t block_page; // 擦除块大小 + } private; +} wl_flash_t; + +extern void wl_flash_init(wl_flash_t *cfg); ///< 初始化 Flash 磨损平衡算法 +extern uint32_t wl_flash_get_current_address(wl_flash_t *cfg); ///< 获取当前写入地址 +extern void wl_flash_set_next_address(wl_flash_t *cfg); ///< 设置下一个写入地址,下一个写入区域会被擦除 +extern void wl_flash_set_current_address(wl_flash_t *cfg, uint32_t address); ///< 设置当前写入地址 +extern void wl_flash_erase(wl_flash_t *cfg); ///< 擦除 Flash, 如果开启了平衡擦写标志, 则会随机分配写入地址 +extern BOOL wl_flash_write(wl_flash_t *cfg, const uint8_t *buf, uint16_t size); ///< 写入 Flash +extern BOOL wl_flash_read(wl_flash_t *cfg, uint8_t *buf, uint16_t size); ///< 读取 Flash +#endif // __WL_FLASH_H__ diff --git a/lib/menu/menu.c b/lib/menu/menu.c new file mode 100644 index 0000000..ec59239 --- /dev/null +++ b/lib/menu/menu.c @@ -0,0 +1,905 @@ +#include "menu.h" +#include "menus_main.h" +#include "entity.h" +#include +#include +#include "convert.h" +#ifdef _COT_MENU_USE_MALLOC_ +#include +#endif + +#ifdef _COT_MENU_USE_SHORTCUT_ +#include +#endif + +/* private typedef ---------------------------------------------------------------------------------------------------*/ +typedef struct menu_ctrl +{ + uint16_t parent_window_no; /*!< 父菜单的窗口号 */ + struct menu_ctrl *p_parent_menu_ctrl; /*!< 父菜单控制处理 */ + char *(psz_desc[MENU_SUPPORT_LANGUAGE]); /*!< 当前选项的字符串描述(多语种) */ + showmenu_call_fun_f pfn_show_menu_fun; /*!< 当前菜单显示效果函数 */ + menu_list_t *p_menu_list; /*!< 当前菜单列表 */ + menu_call_fun_f pfn_load_call_fun; /*!< 当前菜单加载函数 */ + menu_call_fun_f pfn_run_call_fun; /*!< 当前选项的非菜单功能函数 */ + menusize_t items_num; /*!< 当前菜单选项总数目 */ + menusize_t show_base_item; /*!< 当前菜单首个显示的选项 */ + menusize_t select_item; /*!< 当前菜单选中的选项 */ + BOOL is_selected; /*!< 菜单选项是否已经被选择 */ +} menu_ctrl_t; + +typedef struct +{ + menu_ctrl_t *p_menu_ctrl; /*!< 当前菜单控制处理 */ + menu_call_fun_f pfn_main_enter_call_fun; /*!< 主菜单进入时(进入菜单)需要执行一次的函数 */ + menu_call_fun_f pfn_main_exit_call_fun; /*!< 主菜单进入后退出时(退出菜单)需要执行一次的函数 */ + menu_call_fun_f pfn_load_call_fun; /*!< 重加载函数 */ + uint8_t language; /*!< 语种选择 */ + BOOL is_enter_main_menu : TRUE; /*!< 是否进入了主菜单 */ +} menu_manage_t; + +/* private define ----------------------------------------------------------------------------------------------------*/ +/* private macro -----------------------------------------------------------------------------------------------------*/ +/* private variables -------------------------------------------------------------------------------------------------*/ +static menu_manage_t sg_t_menu_manage; + +#ifndef _menu_use_malloc_ +static menu_ctrl_t sg_arr_menu_ctrl[MENU_MAX_DEPTH]; +#endif + +static uint8_t sg_curr_menu_depth = 0; + +/* private function prototypes ---------------------------------------------------------------------------------------*/ +static menu_ctrl_t *new_menu(void); +static void delete_menu(menu_ctrl_t *p_menu); + +/* private function --------------------------------------------------------------------------------------------------*/ +/** + * @brief 新建菜单层级 + * + * @return menu_ctrl_t* + */ +static menu_ctrl_t *new_menu(void) +{ + menu_ctrl_t *p_menu_ctrl = NULL; + + if (sg_curr_menu_depth < MENU_MAX_DEPTH) + { +#ifdef _menu_use_malloc_ + p_menu_ctrl = (menu_ctrl_t *)malloc(sizeof(menu_ctrl_t)); +#else + p_menu_ctrl = &sg_arr_menu_ctrl[sg_curr_menu_depth]; +#endif + sg_curr_menu_depth++; + } + + return p_menu_ctrl; +} + +/** + * @brief 销毁菜单层级 + * + * @param p_menu + */ +static void delete_menu(menu_ctrl_t *p_menu) +{ +#ifdef _menu_use_malloc_ + free(p_menu); +#endif + if (sg_curr_menu_depth > 0) + { + sg_curr_menu_depth--; + } +} + +/** + * @brief 解绑当前菜单 + * @return {*} + * @note + */ +BOOL menu_unbind(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL) + { + return FALSE; + } + + delete_menu(sg_t_menu_manage.p_menu_ctrl); + return TRUE; +} + +/** + * @brief 菜单初始化 + * + * @param[in] p_main_menu 主菜单注册信息 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_init(const main_menu_cfg_t *p_main_menu) +{ + int i; + menu_ctrl_t *p_new_menu_ctrl = NULL; + + if (sg_t_menu_manage.p_menu_ctrl != NULL) + { + return FALSE; + } + +#if MENU_MAX_DEPTH != 0 + sg_curr_menu_depth = 0; +#endif + + if ((p_new_menu_ctrl = new_menu()) == NULL) + { + return FALSE; + } + + sg_t_menu_manage.language = 0; + + for (i = 0; i < MENU_SUPPORT_LANGUAGE; i++) + { + p_new_menu_ctrl->psz_desc[i] = (char *)p_main_menu->psz_desc[i]; + } + + p_new_menu_ctrl->p_parent_menu_ctrl = NULL; + p_new_menu_ctrl->pfn_load_call_fun = p_main_menu->pfn_load_call_fun; + p_new_menu_ctrl->pfn_show_menu_fun = NULL; + p_new_menu_ctrl->pfn_run_call_fun = p_main_menu->pfn_run_call_fun; + + p_new_menu_ctrl->p_menu_list = NULL; + p_new_menu_ctrl->items_num = 0; + p_new_menu_ctrl->select_item = 0; + p_new_menu_ctrl->show_base_item = 0; + + sg_t_menu_manage.p_menu_ctrl = p_new_menu_ctrl; + sg_t_menu_manage.is_enter_main_menu = 0; + sg_t_menu_manage.pfn_main_enter_call_fun = p_main_menu->pfn_enter_call_fun; + sg_t_menu_manage.pfn_main_exit_call_fun = p_main_menu->pfn_exit_call_fun; + sg_t_menu_manage.pfn_load_call_fun = p_new_menu_ctrl->pfn_load_call_fun; + + return TRUE; +} + +/** + * @brief 菜单反初始化 + * + * @attention 不管处于任何界面都会逐级退出到主菜单后(会调用退出函数),再退出主菜单,最后反初始化 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_de_init(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL) + { + return FALSE; + } + + menu_main_exit(); + + delete_menu(sg_t_menu_manage.p_menu_ctrl); + sg_t_menu_manage.p_menu_ctrl = NULL; + sg_t_menu_manage.language = 0; + sg_t_menu_manage.is_enter_main_menu = 0; + sg_t_menu_manage.pfn_main_enter_call_fun = NULL; + sg_t_menu_manage.pfn_main_exit_call_fun = NULL; + sg_t_menu_manage.pfn_load_call_fun = NULL; + + return TRUE; +} + +/** + * @brief 子菜单绑定当前菜单选项 + * + * @param parent_window_no 父菜单选项的窗口号 + * @param p_menu_list 新的菜单列表 + * @param menu_num 新的菜单列表数目 + * @param pfn_show_menu_fun 新的菜单列表显示效果回调函数, 为NULL则延续上级菜单显示效果 + * @return BOOL + */ +BOOL menu_bind(uint16_t parent_window_no, const menu_list_t *p_menu_list, menusize_t menu_num, showmenu_call_fun_f pfn_show_menu_fun) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL) + { + return FALSE; + } + + if (sg_t_menu_manage.p_menu_ctrl->p_menu_list != NULL) + { + return TRUE; + } + + sg_t_menu_manage.p_menu_ctrl->p_menu_list = (menu_list_t *)p_menu_list; + sg_t_menu_manage.p_menu_ctrl->items_num = menu_num; + sg_t_menu_manage.p_menu_ctrl->parent_window_no = parent_window_no; + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + + if (pfn_show_menu_fun != NULL) + { + sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun = pfn_show_menu_fun; + } + + return TRUE; +} + +/** + * @brief 选择语种 + * + * @param[in] language_idx 语种索引 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_select_language(uint8_t language_idx) +{ + if (MENU_SUPPORT_LANGUAGE <= language_idx) + { + return FALSE; + } + + sg_t_menu_manage.language = language_idx; + return TRUE; +} + +/** + * @brief 复位菜单, 回到主菜单界面 + * + * @note 该复位回到主菜单不会执行退出所需要执行的回调函数 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_reset(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + while (sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl != NULL) + { + menu_ctrl_t *p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl; + + sg_t_menu_manage.p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl; + delete_menu(p_menu_ctrl); + } + + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + + return TRUE; +} + +/** + * @brief 主菜单进入 + * + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_main_enter(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 1) + { + return FALSE; + } + + if (sg_t_menu_manage.pfn_main_enter_call_fun != NULL) + { + sg_t_menu_manage.pfn_main_enter_call_fun(); + } + + sg_t_menu_manage.is_enter_main_menu = 1; + sg_t_menu_manage.pfn_load_call_fun = sg_t_menu_manage.p_menu_ctrl->pfn_load_call_fun; + + return TRUE; +} + +/** + * @brief 主菜单退出 + * + * @attention 不管处于任何界面都会逐级退出到主菜单后(会调用退出函数),再退出主菜单 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_main_exit(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + while (menu_exit(1) == 0) + { + } + + if (sg_t_menu_manage.pfn_main_exit_call_fun != NULL) + { + sg_t_menu_manage.pfn_main_exit_call_fun(); + } + + sg_t_menu_manage.is_enter_main_menu = 0; + + return TRUE; +} + +/** + * @brief 进入当前菜单选项 + * + * @param[in] p 传递给进入函数的参数 != NULL 执行进入函数 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_enter(void *p) +{ + int i; + menu_ctrl_t *p_new_menu_ctrl = NULL; + menu_ctrl_t *p_curr_menu_ctrl = sg_t_menu_manage.p_menu_ctrl; + + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if ((p_new_menu_ctrl = new_menu()) == NULL) + { + return FALSE; + } + + for (i = 0; i < MENU_SUPPORT_LANGUAGE; i++) + { + p_new_menu_ctrl->psz_desc[i] = (char *)p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].psz_desc[i]; + } + + p_new_menu_ctrl->p_menu_list = NULL; + p_new_menu_ctrl->items_num = 0; + p_new_menu_ctrl->pfn_show_menu_fun = p_curr_menu_ctrl->pfn_show_menu_fun; + p_new_menu_ctrl->pfn_load_call_fun = p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_load_call_fun; + p_new_menu_ctrl->pfn_run_call_fun = p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_run_call_fun; + p_new_menu_ctrl->select_item = 0; + p_new_menu_ctrl->is_selected = TRUE; + p_new_menu_ctrl->p_parent_menu_ctrl = p_curr_menu_ctrl; + + sg_t_menu_manage.p_menu_ctrl = p_new_menu_ctrl; + sg_t_menu_manage.pfn_load_call_fun = p_new_menu_ctrl->pfn_load_call_fun; + + if (p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_enter_call_fun != NULL) + { + p_curr_menu_ctrl->p_menu_list[p_curr_menu_ctrl->select_item].pfn_enter_call_fun(); + } + + return TRUE; +} + +/** + * @brief 退出当前选项并返回上一层菜单 + * + * @param[in] is_reset 菜单选项是否从头选择 + * @return TRUE:成功;FALSE:失败, 即目前处于主菜单, 无法返回 + */ +BOOL menu_exit(BOOL is_reset) +{ + menu_ctrl_t *p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl; + + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if (sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl == NULL) + { + return FALSE; + } + + sg_t_menu_manage.p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl; + sg_t_menu_manage.pfn_load_call_fun = sg_t_menu_manage.p_menu_ctrl->pfn_load_call_fun; + delete_menu(p_menu_ctrl); + p_menu_ctrl = NULL; + + if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].pfn_exit_call_fun != NULL) + { + sg_t_menu_manage.p_menu_ctrl->is_selected = FALSE; + sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].pfn_exit_call_fun(); + } + + if (is_reset) + { + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + } + + return TRUE; +} + +/** + * @brief 选择上一个菜单选项 + * + * @param[in] is_allow_roll 第一个选项时是否从跳转到最后一个选项 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_select_previous(BOOL is_allow_roll) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if (sg_t_menu_manage.p_menu_ctrl->select_item > 0) + { + sg_t_menu_manage.p_menu_ctrl->select_item--; + } + else + { + if (is_allow_roll) + { + sg_t_menu_manage.p_menu_ctrl->select_item = sg_t_menu_manage.p_menu_ctrl->items_num - 1; + } + else + { + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + return FALSE; + } + } + + return TRUE; +} + +/** + * @brief 选择菜单的上一页 + * @param[in] show_num 每页菜单项数目 + * @return {*} + * @note + */ +BOOL menu_select_previous_page(uint8_t show_num) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + uint8_t page_no = sg_t_menu_manage.p_menu_ctrl->select_item / show_num; + if (page_no > 0) + { + sg_t_menu_manage.p_menu_ctrl->select_item = (page_no - 1) * show_num; + sg_t_menu_manage.p_menu_ctrl->show_base_item = 0; + } + else + { + sg_t_menu_manage.p_menu_ctrl->select_item = ((sg_t_menu_manage.p_menu_ctrl->items_num - 1) / show_num) * show_num; + sg_t_menu_manage.p_menu_ctrl->show_base_item = 0; + } + return TRUE; +} + +/** + * @brief 选择下一个菜单选项 + * + * @param[in] is_allow_roll 最后一个选项时是否跳转到第一个选项 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_select_next(BOOL is_allow_roll) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if (sg_t_menu_manage.p_menu_ctrl->select_item < (sg_t_menu_manage.p_menu_ctrl->items_num - 1)) + { + sg_t_menu_manage.p_menu_ctrl->select_item++; + } + else + { + if (is_allow_roll) + { + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + } + else + { + sg_t_menu_manage.p_menu_ctrl->select_item = sg_t_menu_manage.p_menu_ctrl->items_num - 1; + return FALSE; + } + } + + return TRUE; +} + +/** + * @brief 选择菜单的下一页 + * @param[in] show_num 每页菜单项数目 + * @return {*} + * @note + */ +BOOL menu_select_next_page(uint8_t show_num) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + uint8_t page_no = sg_t_menu_manage.p_menu_ctrl->select_item / show_num; + if (page_no < ((sg_t_menu_manage.p_menu_ctrl->items_num - 1) / show_num)) + { + sg_t_menu_manage.p_menu_ctrl->select_item = (page_no + 1) * show_num; + sg_t_menu_manage.p_menu_ctrl->show_base_item = 0; + } + else + { + sg_t_menu_manage.p_menu_ctrl->select_item = 0; + sg_t_menu_manage.p_menu_ctrl->show_base_item = 0; + } + return TRUE; +} + +/** + * @brief 跳转菜单项 + * + * @param[in] index 菜单项 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_jump_item(uint8_t index) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + sg_t_menu_manage.p_menu_ctrl->select_item = index; + sg_t_menu_manage.p_menu_ctrl->show_base_item = 0; + return TRUE; +} + +#ifdef _menu_use_shortcut_ + +/** + * @brief 相对主菜单或当前菜单通过下级各菜单索引快速进入指定选项 + * + * @param[in] is_absolute 是否采用绝对菜单索引(从主菜单开始) + * @param[in] deep 菜单深度,大于 0 + * @param[in] ... 各级菜单索引值(从0开始), 入参个数由 deep 的值决定 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_shortcut_enter(BOOL is_absolute, uint8_t deep, ...) +{ + uint8_t select_deep = 0; + va_list p_item_list; + menusize_t select_item; + + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if (is_absolute) + { + menu_reset(); + } + + va_start(p_item_list, deep); + + while (select_deep < deep) + { + select_item = va_arg(p_item_list, int); + + if (select_item >= sg_t_menu_manage.p_menu_ctrl->items_num) + { + va_end(p_item_list); + return FALSE; + } + + sg_t_menu_manage.p_menu_ctrl->select_item = select_item; + menu_enter(NULL); + select_deep++; + } + + va_end(p_item_list); + + return TRUE; +} + +#endif + +/** + * @brief 限制当前菜单界面最多显示的菜单数目 + * + * @note 在菜单显示效果回调函数中使用, 使用成员变量 show_base_item 得到显示界面的第一个选项索引 + * @param[in,out] t_menu_show 当前菜单显示信息 + * @param[in,out] show_num 当前菜单中需要显示的选项数目, 根据当前菜单选项的总数得到最终的显示的选项数目 + * @return TRUE:成功;FALSE:失败 + */ +BOOL menu_limit_show_list_num(menu_show_t *pt_menu_show, menusize_t *p_show_num) +{ + if (pt_menu_show == NULL || p_show_num == NULL) + { + return FALSE; + } + + if (*p_show_num > pt_menu_show->items_num) + { + *p_show_num = pt_menu_show->items_num; + } + + if (pt_menu_show->select_item < pt_menu_show->show_base_item) + { + pt_menu_show->show_base_item = pt_menu_show->select_item; + } + else if ((pt_menu_show->select_item - pt_menu_show->show_base_item) >= *p_show_num) + { + pt_menu_show->show_base_item = pt_menu_show->select_item - *p_show_num + 1; + } + else + { + // 保持 + } + pt_menu_show->page_no = pt_menu_show->select_item / *p_show_num; + return TRUE; +} + +/** + * @brief 获取当前父菜单显示信息 + * 如获取当前菜单的二级父菜单信息,level 为2 + * + * @param[out] pt_menu_show 父 n 级菜单显示信息 + * @param[in] level n 级, 大于 0 + * @return int + */ +BOOL menu_query_parent_menu(menu_show_t *pt_menu_show, uint8_t level) +{ + int i; + menu_list_t *p_menu; + menu_ctrl_t *p_menu_ctrl = NULL; + + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + p_menu_ctrl = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl; + + while (level && p_menu_ctrl != NULL) + { + p_menu = p_menu_ctrl->p_menu_list; + pt_menu_show->items_num = p_menu_ctrl->items_num; + pt_menu_show->select_item = p_menu_ctrl->select_item; + pt_menu_show->show_base_item = p_menu_ctrl->show_base_item; + + pt_menu_show->psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language]; + + for (i = 0; i < pt_menu_show->items_num && i < MENU_MAX_NUM; i++) + { + pt_menu_show->psz_items_desc[i] = (char *)p_menu[i].psz_desc[sg_t_menu_manage.language]; + pt_menu_show->p_items_ex_data[i] = p_menu[i].p_extend_data; + } + + p_menu_ctrl = p_menu_ctrl->p_parent_menu_ctrl; + level--; + } + + if (level != 0 && p_menu_ctrl == NULL) + { + return FALSE; + } + + return TRUE; +} + +/** + * @brief 获取当前菜单选项的描述字符串最大长度 + * + * @param[in] pt_show_info 当前菜单显示信息 + * @return uint8_t + */ +uint8_t menu_psz_desc_max_size(menu_show_t *pt_show_info) +{ + uint8_t i; + uint8_t max_size = 0; + + if (pt_show_info == NULL) + { + return 0; + } + + for (i = 0; i < pt_show_info->items_num; i++) + { + if (strlen(pt_show_info->psz_items_desc[i]) > max_size) + { + max_size = strlen(pt_show_info->psz_items_desc[i]); + } + } + + return max_size; +} + +/** + * @brief 获取文本信息 + * @param {char} *s 返回的文本 + * @param {menu_txt_t} *m_txt 文本内容 + * @return {*} + * @note + */ +void menu_txt_show(char *buf, const menu_txt_t *m_txt) +{ + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(m_txt != NULL __DBG_LINE); + sprintf(buf, "%s", m_txt->psz_desc[sg_t_menu_manage.language]); +} + +/** + * @brief 通过当前窗口编号进入菜单 + * + * @param[in] no 选项号 + */ +BOOL menu_enter_with_window_no(uint8_t no) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.p_menu_ctrl->items_num == 0) + { + return FALSE; + } + + for (uint8_t i = 0; i < sg_t_menu_manage.p_menu_ctrl->items_num; i++) + { + if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].window_no == no) + { + sg_t_menu_manage.p_menu_ctrl->select_item = i; + menu_enter(NULL); + return TRUE; + } + else + { + if (sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].window_no != 0 && + sg_t_menu_manage.p_menu_ctrl->p_menu_list[i].single_page != TRUE) + { + sg_t_menu_manage.p_menu_ctrl->select_item = i; + + menu_enter(NULL); + if (menu_enter_with_window_no(no) == FALSE) + { + menu_exit(FALSE); + } + else + { + return TRUE; + } + } + } + } + return FALSE; +} + +/** + * @brief 获取当前窗口号 + * + * @return uint16_t + */ +uint16_t menu_current_window_no(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL) + { + return 0; + } + + return sg_t_menu_manage.p_menu_ctrl->p_menu_list[sg_t_menu_manage.p_menu_ctrl->select_item].window_no; +} + +/** + * @brief 获取当前父窗口号 + * + * @return uint16_t + */ +uint16_t menu_current_parent_window_no(void) +{ + if (sg_t_menu_manage.p_menu_ctrl == NULL) + { + return 0; + } + return sg_t_menu_manage.p_menu_ctrl->parent_window_no; +} + +/** + * @brief 获取当前父窗口信息 + * + * @param[out] info + * @return BOOL + */ +BOOL menu_get_parent_window_info(menu_show_t *info) +{ + DBG_ASSERT(info != NULL __DBG_LINE); + int i; + menu_ctrl_t *p; + menu_list_t *p_menu_list; + if (sg_t_menu_manage.p_menu_ctrl != NULL && sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl != NULL) + { + p = sg_t_menu_manage.p_menu_ctrl->p_parent_menu_ctrl; + p_menu_list = p->p_menu_list; + info->items_num = p->items_num; + info->select_item = p->select_item; + info->show_base_item = p->show_base_item; + + info->psz_desc = p->psz_desc[sg_t_menu_manage.language]; + if (p_menu_list != NULL) + { + for (i = 0; i < info->items_num && i < MENU_MAX_NUM; i++) + { + info->psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language]; + } + } + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 获取当前窗口信息 + * + * @param[out] info + * @return BOOL + */ +BOOL menu_get_current_window_info(menu_show_t *info) +{ + DBG_ASSERT(info != NULL __DBG_LINE); + int i; + menu_list_t *p_menu_list; + if (sg_t_menu_manage.p_menu_ctrl != NULL) + { + p_menu_list = sg_t_menu_manage.p_menu_ctrl->p_menu_list; + info->items_num = sg_t_menu_manage.p_menu_ctrl->items_num; + info->select_item = sg_t_menu_manage.p_menu_ctrl->select_item; + info->show_base_item = sg_t_menu_manage.p_menu_ctrl->show_base_item; + + info->psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language]; + if (p_menu_list != NULL) + { + for (i = 0; i < info->items_num && i < MENU_MAX_NUM; i++) + { + info->psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language]; + info->p_items_ex_data[i] = p_menu_list[i].p_extend_data; + } + } + + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * @brief 菜单任务 + * + * @return 0,成功, 处于菜单模式下; -1,失败, 未处于菜单模式下 + */ +BOOL menu_task(void) +{ + int i; + menu_list_t *p_menu_list; + menu_show_t t_menu_show; + + if (sg_t_menu_manage.p_menu_ctrl == NULL || sg_t_menu_manage.is_enter_main_menu == 0) + { + return FALSE; + } + + if (sg_t_menu_manage.pfn_load_call_fun != NULL) + { + sg_t_menu_manage.pfn_load_call_fun(); + sg_t_menu_manage.pfn_load_call_fun = NULL; + } + + if (sg_t_menu_manage.p_menu_ctrl->p_menu_list != NULL) + { + p_menu_list = sg_t_menu_manage.p_menu_ctrl->p_menu_list; + t_menu_show.items_num = sg_t_menu_manage.p_menu_ctrl->items_num; + t_menu_show.select_item = sg_t_menu_manage.p_menu_ctrl->select_item; + t_menu_show.show_base_item = sg_t_menu_manage.p_menu_ctrl->show_base_item; + + t_menu_show.psz_desc = sg_t_menu_manage.p_menu_ctrl->psz_desc[sg_t_menu_manage.language]; + + for (i = 0; i < t_menu_show.items_num && i < MENU_MAX_NUM; i++) + { + t_menu_show.psz_items_desc[i] = (char *)p_menu_list[i].psz_desc[sg_t_menu_manage.language]; + t_menu_show.p_items_ex_data[i] = p_menu_list[i].p_extend_data; + } + + if (sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun != NULL) + { + sg_t_menu_manage.p_menu_ctrl->pfn_show_menu_fun(&t_menu_show); + } + + sg_t_menu_manage.p_menu_ctrl->show_base_item = t_menu_show.show_base_item; + } + + if (sg_t_menu_manage.p_menu_ctrl->pfn_run_call_fun != NULL) + { + sg_t_menu_manage.p_menu_ctrl->pfn_run_call_fun(); + } + + return TRUE; +} diff --git a/lib/menu/menu.h b/lib/menu/menu.h new file mode 100644 index 0000000..e0b8e5f --- /dev/null +++ b/lib/menu/menu.h @@ -0,0 +1,274 @@ +#ifndef __MENU_H__ +#define __MENU_H__ + +#include "lib.h" + +/******************************************* 配置项 ********************************************************************/ + +/* 定义 _MENU_USE_MALLOC_ 则采用 MALLOC/FREE 的方式实现多级菜单, 否则通过数组的形式 */ +// #define _MENU_USE_MALLOC_ + +/* 定义 _MENU_USE_SHORTCUT_ 则启用快捷菜单选项进入功能 */ +#define _MENU_USE_SHORTCUT_ + +/* 多级菜单深度 */ +#define MENU_MAX_DEPTH 5 + +/* 菜单支持的最大选项数目 */ +#define MENU_MAX_NUM 40 + +/* 菜单支持的语种数目 */ +#define MENU_SUPPORT_LANGUAGE 2 + +/******************************************* 配置项 ********************************************************************/ + +/* exported types ----------------------------------------------------------------------------------------------------*/ + +typedef uint16_t menusize_t; + +typedef void (*menu_call_fun_f)(void); + +typedef struct +{ + menusize_t items_num; /*!< 当前菜单中选项的总数目 */ + + menusize_t select_item; /*!< 当前菜单中被选中的选项 */ + + menusize_t show_base_item; /*!< 当前菜单首个显示的选项 */ + + uint8_t page_no; /*!< 当前菜单的页码 */ + + char *psz_desc; /*!< 当前菜单的字符串描述 */ + + char *psz_items_desc[MENU_MAX_NUM]; /*!< 当前菜单中所有选项的字符串描述 */ + + void *p_items_ex_data[MENU_MAX_NUM]; /*!< 当前菜单中所有选项注册时的扩展数据 */ +} menu_show_t; + +typedef void (*showmenu_call_fun_f)(menu_show_t *pt_show_info); + +typedef struct +{ + const char *(psz_desc[MENU_SUPPORT_LANGUAGE]); +} menu_txt_t; + +/** + * @brief 菜单信息注册结构体 + * + */ +typedef struct +{ + uint16_t window_no; /*!< 当前菜单的窗口号 */ + + BOOL single_page; /*!< 当前菜单是否为单页,TRUE:页面 FALSE:有二级菜单 */ + + const char *(psz_desc[MENU_SUPPORT_LANGUAGE]); /*!< 当前选项的字符串描述(多语种) */ + + menu_call_fun_f pfn_enter_call_fun; /*!< 当前菜单选项进入时(从父菜单进入)需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_exit_call_fun; /*!< 当前菜单选项进入后退出时(退出至父菜单)需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_load_call_fun; /*!< 当前菜单选项每次加载时(从父菜单进入或子菜单退出)需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_run_call_fun; /*!< 当前菜单选项的周期调度函数 */ + + void *p_extend_data; /*!< 当前选项的菜单显示效果函数扩展数据入参, 可自行设置该内容 赋值给p_items_ex_data*/ +} menu_list_t, menu_item_t; + +/** + * @brief 菜单信息注册结构体 + * + */ +typedef struct +{ + const char *(psz_desc[MENU_SUPPORT_LANGUAGE]); /*!< 当前选项的字符串描述(多语种) */ + + menu_call_fun_f pfn_enter_call_fun; /*!< 主前菜单进入时(进入菜单)需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_exit_call_fun; /*!< 主前菜单进入后退出时(退出菜单)需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_load_call_fun; /*!< 主菜单每次加载时需要执行一次的函数, 为null不执行 */ + + menu_call_fun_f pfn_run_call_fun; /*!< 主菜单周期调度函数 */ +} main_menu_cfg_t; + +/* exported constants ------------------------------------------------------------------------------------------------*/ +/* exported macro ----------------------------------------------------------------------------------------------------*/ + +#define COT_GET_MENU_NUM(x) (sizeof(x) / sizeof(menu_list_t)) + +/* exported functions ------------------------------------------------------------------------------------------------*/ + +/* 菜单初始化和反初始化 */ + +extern BOOL menu_init(const main_menu_cfg_t *p_main_menu); +extern BOOL menu_de_init(void); + +extern BOOL menu_unbind(void); + +extern BOOL menu_bind(uint16_t parent_window_no, const menu_list_t *p_menu_list, menusize_t menu_num, showmenu_call_fun_f pfn_show_menu_fun); + +/* 菜单功能设置 */ + +extern BOOL menu_select_language(uint8_t language_idx); + +/* 菜单选项显示时需要使用的功能扩展函数 */ + +extern BOOL menu_limit_show_list_num(menu_show_t *pt_menu_show, menusize_t *p_show_num); +extern BOOL menu_query_parent_menu(menu_show_t *pt_menu_show, uint8_t level); + +/* 菜单操作 */ + +/** + * @brief 进入主菜单 + * @return 进入成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_main_enter(void); + +/** + * @brief 退出主菜单 + * @return 退出成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_main_exit(void); + +/** + * @brief 重置菜单 + * @return 重置成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_reset(void); + +/** + * @brief 进入菜单 + * @param p 指向菜单的指针 + * @return 进入成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_enter(void *p); + +/** + * @brief 退出菜单 + * @param is_reset 是否重置菜单 + * @return 退出成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_exit(BOOL is_reset); + +/** + * @brief 选择上一个菜单项 + * @param is_allow_roll 是否允许循环选择 + * @return 选择成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_select_previous(BOOL is_allow_roll); + +/** + * @brief 选择上一个菜单页 + * @param {uint8_t} show_num 每页菜单项数目 + * @return {*} + * @note + */ +extern BOOL menu_select_previous_page(uint8_t show_num); + +/** + * @brief 选择下一个菜单项 + * @param is_allow_roll 是否允许循环选择 + * @return 选择成功返回TRUE,否则返回FALSE + */ +extern BOOL menu_select_next(BOOL is_allow_roll); + +/** + * @brief 选择下一个菜单页 + * @param {uint8_t} show_num 每页菜单项数目 + * @return {*} + * @note + */ +extern BOOL menu_select_next_page(uint8_t show_num); + +/** + * @brief 跳转到指定菜单页 + * @param {uint8_t} index 菜单项 + * @return {*} + * @note + */ +extern BOOL menu_jump_item(uint8_t index); +/** + * @brief 进入菜单快捷方式 + * + * 该函数用于进入菜单的快捷方式。 + * + * @param is_absolute 是否为绝对路径 + * @param deep 菜单路径的深度 + * @param ... 菜单路径的参数列表 + * @return BOOL 进入菜单是否成功 + */ +extern BOOL menu_shortcut_enter(BOOL is_absolute, uint8_t deep, ...); + +/** + * @brief 获取菜单描述的最大长度 + * + * 该函数用于获取菜单描述的最大长度。 + * + * @param pt_show_info 菜单显示信息的指针 + * @return uint8_t 菜单描述的最大长度 + */ +extern uint8_t menu_psz_desc_max_size(menu_show_t *pt_show_info); + +/** + * @brief 显示菜单文本 + * + * 该函数用于显示菜单的文本。 + * + * @param buf 存储菜单文本的缓冲区 + * @param m_txt 菜单文本的指针 + */ +extern void menu_txt_show(char *buf, const menu_txt_t *m_txt); + +/** + * @brief 进入指定窗口的菜单 + * + * 该函数用于进入指定窗口的菜单。 + * + * @param no 窗口编号 + * @return BOOL 进入菜单是否成功 + */ +extern BOOL menu_enter_with_window_no(uint8_t no); + +/** + * @brief 获取当前父窗口的编号 + * + * 该函数用于获取当前父窗口的编号。 + * + * @return uint16_t 当前父窗口的编号 + */ +extern uint16_t menu_current_parent_window_no(void); + +/** + * @brief 获取当前窗口的编号 + * + * 该函数用于获取当前窗口的编号。 + * + * @return uint16_t 当前窗口的编号 + */ +extern uint16_t menu_current_window_no(void); + +/** + * @brief 获取当前父窗口的信息 + * + * 该函数用于获取当前父窗口的信息。 + * + * @param info 存储当前窗口信息的指针 + * @return BOOL 获取当前窗口信息是否成功 + */ +extern BOOL menu_get_parent_window_info(menu_show_t *info); + +/** + * @brief 获取当前窗口的信息 + * + * 该函数用于获取当前窗口的信息。 + * + * @param info 存储当前窗口信息的指针 + * @return BOOL 获取当前窗口信息是否成功 + */ +extern BOOL menu_get_current_window_info(menu_show_t *info); +/* 菜单轮询处理任务 */ + +extern BOOL menu_task(void); + +#endif // __MENU_H__ diff --git a/lib/readme.md b/lib/readme.md new file mode 100644 index 0000000..94587c6 --- /dev/null +++ b/lib/readme.md @@ -0,0 +1,169 @@ +[TOC] + + +### clist 简单链表 +#### 说明 +- clist是list的简化版,下面给出常用的接口说明 +- 接口说明: +```code + +void clist_init(clist_node_t **ppFirst); ///< 初始化 ,构造一条空的链表 + +void clist_print(clist_node_t *First); ///< 打印链表 + +uint32_t clist_node_count(clist_node_t *First); ///< 获取链表节点数 + +void clist_push_back(clist_node_t **ppFirst, cnode data); ///< 尾部插入 + +void clist_push_front(clist_node_t **ppFirst, cnode data); ///< 头部插入 + +void clist_pop_back(clist_node_t **ppFirst); ///< 尾部删除 + +void clist_pop_front(clist_node_t **ppFirst); ///< 头部删除 + +void clist_insert_for_node(clist_node_t **ppFirst, clist_node_t *pPos, cnode data); ///< 给定结点插入,插入到结点前 + +int32_t clist_insert(clist_node_t **ppFirst, int32_t Pos, cnode data); ///< 按位置插入 + +void clist_erase_for_node(clist_node_t **ppFirst, clist_node_t *pPos); ///< 给定结点删除 + +void clist_remove(clist_node_t **ppFirst, cnode data); ///< 按值删除,只删遇到的第一个 + +void clist_remove_all(clist_node_t **ppFirst, cnode data); ///< 按值删除,删除所有的 + +void clist_destroy(clist_node_t **ppFirst); ///< 销毁 ,需要销毁每一个节点 + +clist_node_t *clist_find(clist_node_t *pFirst, cnode data); ///< 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL + +``` +#### DEMO +- 详细使用请见simple_clist.c +- examples目录内执行```make clist && make run``` + + +### cmd 命令解析器 +#### 说明 +- cmd是一个非常简单好用的命令解析器。 +> 简单来说,我希望我的开发板,可以通过命令执行一些处理,比如说我用串口发一个命令A,开发板就执行A的一些处理,或者,在调试某些AT模组的时候,当我收到模组返回的一些指令后,自动执行一些处理。 +- 接口说明: +```code +REGISTER_CMD(cmd, handler, desc) ///< 注册命令 + +void cmd_init(void); ///< 初始化命令 + +void cmd_parsing(char *str); ///< 命令解析 +``` + +#### DEMO +- 详细使用请见simple_cmd.c +- 该模块不提供GCC编译,只提供KEIL编译和IAR编译 + + + +### data_analysis 数据分析器 +#### 说明 +- data_analysis 是用来处理串口或者其他方式获得的数据,具有识别数据帧起始和结束的功能,只需要定义好识别符以及长度,该模块会将完整的数据帧处理好 +- 接口说明: +```code +typedef void (*data_interupt_cb_t)(uint8_t id, uint8_t ch); ///< 中断回调函数,数据从这里写入 + +extern uint8_t data_read(uint8_t id, void *buffer, uint16_t len); ///< 读取数据 + +extern void data_write(uint8_t id, uint8_t *const string, uint16_t len); ///< TODO 写入数据 + +extern void lock_data(uint8_t data_id); ///< 锁定数据,防止中断写入数据 + +extern void unlock_data(uint8_t data_id); ///< 解锁数据 + +extern data_interupt_cb_t data_fsm_init(uint8_t data_id); ///< 初始化数据状态机 + +extern bool data_reg(uint8_t id, data_reg_t reg); ///< 注册数据 + +extern void data_unreg(uint8_t id); ///< 注销数据 +``` +#### DEMO +- 详细使用请见simple_data_analysis.c +- examples目录内执行```make data_analysis && make run``` + + +### sqqueue(队列) +#### 说明 +- sqqueue 是一个成员变量固定长度的队列 +- 使用的时候先创建一个"sqqueue_ctrl_t"类型的队列对象,调用初始化函数后就可以使用对象中的功能了 + +#### DEMO +- 详细使用请见simple_sqqueue.c +- examples目录内执行```make sqqueue && make run``` + + +### aes(加密) +百度百科 + +#### 说明 + +- 对不同长度的密钥,AES采用不同的加密轮次: + +| 128位 | 192位 | 256位 | +| ------------- | -----------: | :--------: | +| 10 | 12 | 14 | + +- 密钥扩展:简单说就是将原来的密钥扩展到足够用的长度: + + - 128位密钥: 扩展到 11x4x4 + - 192位密钥: 扩展到 13x4x4 + - 256位密钥: 扩展到 15x4x4 + +- AES加密过程是在一个4×4的字节矩阵上运作,所以一次加解密传入的最小块单位为16字节 + + +#### 代码流程 + +```flow +st=>start: 加密开始 +e=>end: 加密结束 +op1=>operation: 加密的数据[简称密文]、密钥(16个字节)、输入数据块和输出数据块 +op2=>operation: 调用接口"aes_set_key",完成密钥的扩展 +op3=>operation: 调用接口"aes_encrypt",完成数据的加密 +st->op1->op2->op3->e +``` +```flow +st=>start: 解密开始 +e=>end: 解密结束 +op1=>operation: 解密的数据[简称密文]、密钥(16个字节)、输入数据块和输出数据块 +op2=>operation: 调用接口"aes_set_key",完成密钥的扩展 +op3=>operation: 调用接口"aes_encrypt",完成数据的解密 +st->op1->op2->op3->e +``` +#### DEMO +- demo中会使用aes中的接口完成简单的加密和解密数据 +- **将aes.h中的AES_ENC_PREKEYED和AES_DEC_PREKEYED置1** +- 详细使用请见simple_aes.c +- examples目录内执行```make aes && make run``` + +### cmac(类CRC) +#### 说明 + +- cmac是一种数据传输检错功能,以保证数据传输的正确性和完整性 +- cmac基本原理是:通过对需要校验的数据奇偶校验、位移、AES加密获取一段16个字节大小的数组 +- **lorawan使用cmac模块给MAC数据和加密数据做校验的优点:效率很高、安全性很高(在校验数据之前调用"AES_CMAC_Update"二次,增加了异变因子)** +- **困惑:为什么不使用CRC32** +- demo中只对需要校验的数据使用"AES_CMAC_Update"一次,无法确定lorawan增加一次"AES_CMAC_Update"的效果如何 + + +#### 代码流程 + +```flow +st=>start: 校验开始 +e=>end: 校验结束 +op1=>operation: 需要校验的数据、密钥、密钥扩展表 +op2=>operation: 调用接口"AES_CMAC_Init",完成密钥扩展表的初始化 +op3=>operation: 调用接口"AES_CMAC_SetKey",完成密钥扩展表数据 +op4=>operation: 调用接口"AES_CMAC_Update",完成数据的奇偶校验 +op5=>operation: 调用接口"AES_CMAC_Final",生成16个字节的校验表 +op6=>operation: 取表4个字节作为校验码 +st->op1->op2->op3->op4->op5->op6->e +``` + +#### DEMO +- 详细使用请见simple_cmac.c +- examples目录内执行```make cmac && make run``` diff --git a/lib/src/aes.c b/lib/src/aes.c new file mode 100644 index 0000000..bf5fbdf --- /dev/null +++ b/lib/src/aes.c @@ -0,0 +1,981 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 0 +#define HAVE_MEMCPY +#include +#if defined(_MSC_VER) +#include +#pragma intrinsic(memcpy) +#endif +#endif + +#include +#include + +/* define if you have fast 32-bit types on your system */ +#if (__CORTEX_M != 0) // if Cortex is different from M0/M0+ +#define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +#define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +#define VERSION_1 +#endif + +#include "aes.h" + +// #if defined( HAVE_UINT_32T ) +// typedef unsigned long uint32_t; +// #endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x)&1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined(USE_TABLES) + +#define sb_data(w) \ + { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), \ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76), \ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), \ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0), \ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc), \ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), \ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), \ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75), \ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), \ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), \ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b), \ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), \ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), \ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), \ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5), \ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2), \ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17), \ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), \ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), \ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), \ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79), \ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9), \ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08), \ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6), \ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), \ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), \ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), \ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94), \ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), \ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68), \ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) \ + } + +#define isb_data(w) \ + { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), \ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb), \ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87), \ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb), \ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d), \ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), \ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), \ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25), \ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16), \ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92), \ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda), \ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), \ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), \ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06), \ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02), \ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b), \ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea), \ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73), \ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), \ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), \ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), \ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b), \ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20), \ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4), \ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31), \ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), \ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), \ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), \ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0), \ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61), \ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26), \ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) \ + } + +#define mm_data(w) \ + { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), \ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f), \ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17), \ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f), \ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27), \ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f), \ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37), \ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f), \ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47), \ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f), \ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57), \ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f), \ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67), \ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f), \ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77), \ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f), \ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87), \ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f), \ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97), \ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), \ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), \ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf), \ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7), \ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf), \ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7), \ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf), \ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), \ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), \ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7), \ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef), \ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7), \ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) \ + } + +static const uint8_t sbox[256] = sb_data(f1); + +#if defined(AES_DEC_PREKEYED) +static const uint8_t isbox[256] = isb_data(f1); +#endif + +static const uint8_t gfm2_sbox[256] = sb_data(f2); +static const uint8_t gfm3_sbox[256] = sb_data(f3); + +#if defined(AES_DEC_PREKEYED) +static const uint8_t gfmul_9[256] = mm_data(f9); +static const uint8_t gfmul_b[256] = mm_data(fb); +static const uint8_t gfmul_d[256] = mm_data(fd); +static const uint8_t gfmul_e[256] = mm_data(fe); +#endif + +#define s_box(x) sbox[(x)] +#if defined(AES_DEC_PREKEYED) +#define is_box(x) isbox[(x)] +#endif +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#if defined(AES_DEC_PREKEYED) +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] +#endif +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint8_t hibit(const uint8_t x) +{ + uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) +{ + uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if (x < 2) + return x; + + for (;;) + { + if (n1) + while (n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if (n2) /* repeat with values swapped */ + while (n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) +{ +#if defined(HAVE_UINT_32T) + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint8_t inv_affine(const uint8_t x) +{ +#if defined(HAVE_UINT_32T) + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined(HAVE_MEMCPY) +#define block_copy_nn(d, s, l) memcpy(d, s, l) +#define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +#define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +#define block_copy(d, s) copy_block(d, s) +#endif + +static void copy_block(void *d, const void *s) +{ +#if defined(HAVE_UINT_32T) + ((uint32_t *)d)[0] = ((uint32_t *)s)[0]; + ((uint32_t *)d)[1] = ((uint32_t *)s)[1]; + ((uint32_t *)d)[2] = ((uint32_t *)s)[2]; + ((uint32_t *)d)[3] = ((uint32_t *)s)[3]; +#else + ((uint8_t *)d)[0] = ((uint8_t *)s)[0]; + ((uint8_t *)d)[1] = ((uint8_t *)s)[1]; + ((uint8_t *)d)[2] = ((uint8_t *)s)[2]; + ((uint8_t *)d)[3] = ((uint8_t *)s)[3]; + ((uint8_t *)d)[4] = ((uint8_t *)s)[4]; + ((uint8_t *)d)[5] = ((uint8_t *)s)[5]; + ((uint8_t *)d)[6] = ((uint8_t *)s)[6]; + ((uint8_t *)d)[7] = ((uint8_t *)s)[7]; + ((uint8_t *)d)[8] = ((uint8_t *)s)[8]; + ((uint8_t *)d)[9] = ((uint8_t *)s)[9]; + ((uint8_t *)d)[10] = ((uint8_t *)s)[10]; + ((uint8_t *)d)[11] = ((uint8_t *)s)[11]; + ((uint8_t *)d)[12] = ((uint8_t *)s)[12]; + ((uint8_t *)d)[13] = ((uint8_t *)s)[13]; + ((uint8_t *)d)[14] = ((uint8_t *)s)[14]; + ((uint8_t *)d)[15] = ((uint8_t *)s)[15]; +#endif +} + +static void copy_block_nn(uint8_t *d, const uint8_t *s, uint8_t nn) +{ + while (nn--) + //*((uint8_t*)d)++ = *((uint8_t*)s)++; + *d++ = *s++; +} + +static void xor_block(void *d, const void *s) +{ +#if defined(HAVE_UINT_32T) + ((uint32_t *)d)[0] ^= ((uint32_t *)s)[0]; + ((uint32_t *)d)[1] ^= ((uint32_t *)s)[1]; + ((uint32_t *)d)[2] ^= ((uint32_t *)s)[2]; + ((uint32_t *)d)[3] ^= ((uint32_t *)s)[3]; +#else + ((uint8_t *)d)[0] ^= ((uint8_t *)s)[0]; + ((uint8_t *)d)[1] ^= ((uint8_t *)s)[1]; + ((uint8_t *)d)[2] ^= ((uint8_t *)s)[2]; + ((uint8_t *)d)[3] ^= ((uint8_t *)s)[3]; + ((uint8_t *)d)[4] ^= ((uint8_t *)s)[4]; + ((uint8_t *)d)[5] ^= ((uint8_t *)s)[5]; + ((uint8_t *)d)[6] ^= ((uint8_t *)s)[6]; + ((uint8_t *)d)[7] ^= ((uint8_t *)s)[7]; + ((uint8_t *)d)[8] ^= ((uint8_t *)s)[8]; + ((uint8_t *)d)[9] ^= ((uint8_t *)s)[9]; + ((uint8_t *)d)[10] ^= ((uint8_t *)s)[10]; + ((uint8_t *)d)[11] ^= ((uint8_t *)s)[11]; + ((uint8_t *)d)[12] ^= ((uint8_t *)s)[12]; + ((uint8_t *)d)[13] ^= ((uint8_t *)s)[13]; + ((uint8_t *)d)[14] ^= ((uint8_t *)s)[14]; + ((uint8_t *)d)[15] ^= ((uint8_t *)s)[15]; +#endif +} + +static void copy_and_key(void *d, const void *s, const void *k) +{ +#if defined(HAVE_UINT_32T) + ((uint32_t *)d)[0] = ((uint32_t *)s)[0] ^ ((uint32_t *)k)[0]; + ((uint32_t *)d)[1] = ((uint32_t *)s)[1] ^ ((uint32_t *)k)[1]; + ((uint32_t *)d)[2] = ((uint32_t *)s)[2] ^ ((uint32_t *)k)[2]; + ((uint32_t *)d)[3] = ((uint32_t *)s)[3] ^ ((uint32_t *)k)[3]; +#elif 1 + ((uint8_t *)d)[0] = ((uint8_t *)s)[0] ^ ((uint8_t *)k)[0]; + ((uint8_t *)d)[1] = ((uint8_t *)s)[1] ^ ((uint8_t *)k)[1]; + ((uint8_t *)d)[2] = ((uint8_t *)s)[2] ^ ((uint8_t *)k)[2]; + ((uint8_t *)d)[3] = ((uint8_t *)s)[3] ^ ((uint8_t *)k)[3]; + ((uint8_t *)d)[4] = ((uint8_t *)s)[4] ^ ((uint8_t *)k)[4]; + ((uint8_t *)d)[5] = ((uint8_t *)s)[5] ^ ((uint8_t *)k)[5]; + ((uint8_t *)d)[6] = ((uint8_t *)s)[6] ^ ((uint8_t *)k)[6]; + ((uint8_t *)d)[7] = ((uint8_t *)s)[7] ^ ((uint8_t *)k)[7]; + ((uint8_t *)d)[8] = ((uint8_t *)s)[8] ^ ((uint8_t *)k)[8]; + ((uint8_t *)d)[9] = ((uint8_t *)s)[9] ^ ((uint8_t *)k)[9]; + ((uint8_t *)d)[10] = ((uint8_t *)s)[10] ^ ((uint8_t *)k)[10]; + ((uint8_t *)d)[11] = ((uint8_t *)s)[11] ^ ((uint8_t *)k)[11]; + ((uint8_t *)d)[12] = ((uint8_t *)s)[12] ^ ((uint8_t *)k)[12]; + ((uint8_t *)d)[13] = ((uint8_t *)s)[13] ^ ((uint8_t *)k)[13]; + ((uint8_t *)d)[14] = ((uint8_t *)s)[14] ^ ((uint8_t *)k)[14]; + ((uint8_t *)d)[15] = ((uint8_t *)s)[15] ^ ((uint8_t *)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key(uint8_t d[N_BLOCK], const uint8_t k[N_BLOCK]) +{ + xor_block(d, k); +} + +static void shift_sub_rows(uint8_t st[N_BLOCK]) +{ + uint8_t tt; + + st[0] = s_box(st[0]); + st[4] = s_box(st[4]); + st[8] = s_box(st[8]); + st[12] = s_box(st[12]); + + tt = st[1]; + st[1] = s_box(st[5]); + st[5] = s_box(st[9]); + st[9] = s_box(st[13]); + st[13] = s_box(tt); + + tt = st[2]; + st[2] = s_box(st[10]); + st[10] = s_box(tt); + tt = st[6]; + st[6] = s_box(st[14]); + st[14] = s_box(tt); + + tt = st[15]; + st[15] = s_box(st[11]); + st[11] = s_box(st[7]); + st[7] = s_box(st[3]); + st[3] = s_box(tt); +} + +#if defined(AES_DEC_PREKEYED) + +static void inv_shift_sub_rows(uint8_t st[N_BLOCK]) +{ + uint8_t tt; + + st[0] = is_box(st[0]); + st[4] = is_box(st[4]); + st[8] = is_box(st[8]); + st[12] = is_box(st[12]); + + tt = st[13]; + st[13] = is_box(st[9]); + st[9] = is_box(st[5]); + st[5] = is_box(st[1]); + st[1] = is_box(tt); + + tt = st[2]; + st[2] = is_box(st[10]); + st[10] = is_box(tt); + tt = st[6]; + st[6] = is_box(st[14]); + st[14] = is_box(tt); + + tt = st[3]; + st[3] = is_box(st[7]); + st[7] = is_box(st[11]); + st[11] = is_box(st[15]); + st[15] = is_box(tt); +} + +#endif + +#if defined(VERSION_1) +static void mix_sub_columns(uint8_t dt[N_BLOCK]) +{ + uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else +static void mix_sub_columns(uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK]) +{ +#endif + dt[0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); +} + +#if defined(AES_DEC_PREKEYED) + +#if defined(VERSION_1) +static void inv_mix_sub_columns(uint8_t dt[N_BLOCK]) +{ + uint8_t st[N_BLOCK]; + block_copy(st, dt); +#else +static void inv_mix_sub_columns(uint8_t dt[N_BLOCK], uint8_t st[N_BLOCK]) +{ +#endif + dt[0] = is_box(gfm_e(st[0]) ^ gfm_b(st[1]) ^ gfm_d(st[2]) ^ gfm_9(st[3])); + dt[5] = is_box(gfm_9(st[0]) ^ gfm_e(st[1]) ^ gfm_b(st[2]) ^ gfm_d(st[3])); + dt[10] = is_box(gfm_d(st[0]) ^ gfm_9(st[1]) ^ gfm_e(st[2]) ^ gfm_b(st[3])); + dt[15] = is_box(gfm_b(st[0]) ^ gfm_d(st[1]) ^ gfm_9(st[2]) ^ gfm_e(st[3])); + + dt[4] = is_box(gfm_e(st[4]) ^ gfm_b(st[5]) ^ gfm_d(st[6]) ^ gfm_9(st[7])); + dt[9] = is_box(gfm_9(st[4]) ^ gfm_e(st[5]) ^ gfm_b(st[6]) ^ gfm_d(st[7])); + dt[14] = is_box(gfm_d(st[4]) ^ gfm_9(st[5]) ^ gfm_e(st[6]) ^ gfm_b(st[7])); + dt[3] = is_box(gfm_b(st[4]) ^ gfm_d(st[5]) ^ gfm_9(st[6]) ^ gfm_e(st[7])); + + dt[8] = is_box(gfm_e(st[8]) ^ gfm_b(st[9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[8]) ^ gfm_e(st[9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[2] = is_box(gfm_d(st[8]) ^ gfm_9(st[9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[7] = is_box(gfm_b(st[8]) ^ gfm_d(st[9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); +} + +#endif + +#if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED) + +/* Set the cipher key for the pre-keyed version */ + +return_type aes_set_key(const uint8_t key[], length_type keylen, aes_context ctx[1]) +{ + uint8_t cc, rc, hi; + + switch (keylen) + { + case 16: + case 24: + case 32: + break; + default: + ctx->rnd = 0; + return (uint8_t)-1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for (cc = keylen, rc = 1; cc < hi; cc += 4) + { + uint8_t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if (cc % keylen == 0) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } + else if (keylen > 24 && cc % keylen == 16) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined(AES_ENC_PREKEYED) + +/* Encrypt a single block of 16 bytes */ + +return_type aes_encrypt(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1]) +{ + if (ctx->rnd) + { + uint8_t s1[N_BLOCK], r; + copy_and_key(s1, in, ctx->ksch); + + for (r = 1; r < ctx->rnd; ++r) +#if defined(VERSION_1) + { + mix_sub_columns(s1); + add_round_key(s1, ctx->ksch + r * N_BLOCK); + } +#else + { + uint8_t s2[N_BLOCK]; + mix_sub_columns(s2, s1); + copy_and_key(s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows(s1); + copy_and_key(out, s1, ctx->ksch + r * N_BLOCK); + } + else + return (uint8_t)-1; + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt(const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1]) +{ + + while (n_block--) + { + xor_block(iv, in); + if (aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + // memcpy(out, iv, N_BLOCK); + block_copy(out, iv); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_DEC_PREKEYED) + +/* Decrypt a single block of 16 bytes */ + +return_type aes_decrypt(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], const aes_context ctx[1]) +{ + if (ctx->rnd) + { + uint8_t s1[N_BLOCK], r; + copy_and_key(s1, in, ctx->ksch + ctx->rnd * N_BLOCK); + inv_shift_sub_rows(s1); + + for (r = ctx->rnd; --r;) +#if defined(VERSION_1) + { + add_round_key(s1, ctx->ksch + r * N_BLOCK); + inv_mix_sub_columns(s1); + } +#else + { + uint8_t s2[N_BLOCK]; + copy_and_key(s2, s1, ctx->ksch + r * N_BLOCK); + inv_mix_sub_columns(s1, s2); + } +#endif + copy_and_key(out, s1, ctx->ksch); + return 0; + } + else + return 1; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt(const uint8_t *in, uint8_t *out, + int32_t n_block, uint8_t iv[N_BLOCK], const aes_context ctx[1]) +{ + while (n_block--) + { + uint8_t tmp[N_BLOCK]; + + // memcpy(tmp, in, N_BLOCK); + block_copy(tmp, in); + if (aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + // memcpy(iv, tmp, N_BLOCK); + block_copy(iv, tmp); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_ENC_128_OTFK) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128(uint8_t k[N_BLOCK], uint8_t *rc) +{ + uint8_t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2(*rc); + + for (cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_encrypt_128(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK]) +{ + uint8_t s1[N_BLOCK], r, rc = 1; + + if (o_key != key) + block_copy(o_key, key); + copy_and_key(s1, in, o_key); + + for (r = 1; r < 10; ++r) +#if defined(VERSION_1) + { + mix_sub_columns(s1); + update_encrypt_key_128(o_key, &rc); + add_round_key(s1, o_key); + } +#else + { + uint8_t s2[N_BLOCK]; + mix_sub_columns(s2, s1); + update_encrypt_key_128(o_key, &rc); + copy_and_key(s1, s2, o_key); + } +#endif + + shift_sub_rows(s1); + update_encrypt_key_128(o_key, &rc); + copy_and_key(out, s1, o_key); +} + +#endif + +#if defined(AES_DEC_128_OTFK) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128(uint8_t k[N_BLOCK], uint8_t *rc) +{ + uint8_t cc; + + for (cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_decrypt_128(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[N_BLOCK], uint8_t o_key[N_BLOCK]) +{ + uint8_t s1[N_BLOCK], r, rc = 0x6c; + if (o_key != key) + block_copy(o_key, key); + + copy_and_key(s1, in, o_key); + inv_shift_sub_rows(s1); + + for (r = 10; --r;) +#if defined(VERSION_1) + { + update_decrypt_key_128(o_key, &rc); + add_round_key(s1, o_key); + inv_mix_sub_columns(s1); + } +#else + { + uint8_t s2[N_BLOCK]; + update_decrypt_key_128(o_key, &rc); + copy_and_key(s2, s1, o_key); + inv_mix_sub_columns(s1, s2); + } +#endif + update_decrypt_key_128(o_key, &rc); + copy_and_key(out, s1, o_key); +} + +#endif + +#if defined(AES_ENC_256_OTFK) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256(uint8_t k[2 * N_BLOCK], uint8_t *rc) +{ + uint8_t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2(*rc); + + for (cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for (cc = 20; cc < 32; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void aes_encrypt_256(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK]) +{ + uint8_t s1[N_BLOCK], r, rc = 1; + if (o_key != key) + { + block_copy(o_key, key); + block_copy(o_key + 16, key + 16); + } + copy_and_key(s1, in, o_key); + + for (r = 1; r < 14; ++r) +#if defined(VERSION_1) + { + mix_sub_columns(s1); + if (r & 1) + add_round_key(s1, o_key + 16); + else + { + update_encrypt_key_256(o_key, &rc); + add_round_key(s1, o_key); + } + } +#else + { + uint8_t s2[N_BLOCK]; + mix_sub_columns(s2, s1); + if (r & 1) + copy_and_key(s1, s2, o_key + 16); + else + { + update_encrypt_key_256(o_key, &rc); + copy_and_key(s1, s2, o_key); + } + } +#endif + + shift_sub_rows(s1); + update_encrypt_key_256(o_key, &rc); + copy_and_key(out, s1, o_key); +} + +#endif + +#if defined(AES_DEC_256_OTFK) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256(uint8_t k[2 * N_BLOCK], uint8_t *rc) +{ + uint8_t cc; + + for (cc = 28; cc > 16; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for (cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void aes_decrypt_256(const uint8_t in[N_BLOCK], uint8_t out[N_BLOCK], + const uint8_t key[2 * N_BLOCK], uint8_t o_key[2 * N_BLOCK]) +{ + uint8_t s1[N_BLOCK], r, rc = 0x80; + + if (o_key != key) + { + block_copy(o_key, key); + block_copy(o_key + 16, key + 16); + } + + copy_and_key(s1, in, o_key); + inv_shift_sub_rows(s1); + + for (r = 14; --r;) +#if defined(VERSION_1) + { + if ((r & 1)) + { + update_decrypt_key_256(o_key, &rc); + add_round_key(s1, o_key + 16); + } + else + add_round_key(s1, o_key); + inv_mix_sub_columns(s1); + } +#else + { + uint8_t s2[N_BLOCK]; + if ((r & 1)) + { + update_decrypt_key_256(o_key, &rc); + copy_and_key(s2, s1, o_key + 16); + } + else + copy_and_key(s2, s1, o_key); + inv_mix_sub_columns(s1, s2); + } +#endif + copy_and_key(out, s1, o_key); +} + +#endif diff --git a/lib/src/clist.c b/lib/src/clist.c new file mode 100644 index 0000000..fb54261 --- /dev/null +++ b/lib/src/clist.c @@ -0,0 +1,338 @@ +/** + * @file clist.c + * @author xxx + * @date 2023-08-08 23:18:09 + * @brief + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "clist.h" + +/** + * @brief 初始化链表 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @return void + * @note 初始化链表,将链表头节点设置为空 + */ +void clist_init(clist_node_t **ppFirst) +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + *ppFirst = NULL; +} + +/** + * @brief 打印链表 + * @param {clist_node_t} First 链表头节点 + * @return void + * @note 打印链表中的所有节点数据 + */ +void clist_print(clist_node_t *First) +{ + LOG_PRINT("list: "); + for (clist_node_t *p = First; p != NULL; p = p->Next) + LOG_PRINT("%d-->", p->data); + LOG_PRINT("\n"); +} + +/** + * @brief 获取链表节点数 + * @param {clist_node_t} First 链表头节点 + * @return {uint32_t} 链表节点数 + * @note 遍历链表,计算节点数 + */ +uint32_t clist_node_count(clist_node_t *First) +{ + int32_t count = 0; + for (clist_node_t *p = First; p != NULL; p = p->Next) + count++; + return count; +} + +/** + * @brief 申请新节点 + * @param {cnode} data 节点数据 + * @return {clist_node_t *} 新节点指针 + * @note 分配内存,创建新节点 + */ +static clist_node_t *CreateNode(cnode data) +{ + clist_node_t *node = (clist_node_t *)osel_mem_alloc(sizeof(clist_node_t)); + node->data = data; + node->Next = NULL; + return node; +} + +/** + * @brief 尾部插入 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @param {cnode} data 要插入的节点数据 + * @return void + * @note 在链表末尾插入一个新节点,新节点数据为data + */ +void clist_push_back(clist_node_t **ppFirst, cnode data) +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + clist_node_t *node = CreateNode(data); + if (*ppFirst == NULL) // 判断链表不为空 + { + *ppFirst = node; + return; + } + // 找链表中的最后一个节点 + clist_node_t *p = *ppFirst; + while (p->Next != NULL) + p = p->Next; + p->Next = node; // 插入新申请的节点 +} + +/** + * @brief 头部插入 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @param {cnode} data 要插入的节点数据 + * @return void + * @note 在链表头部插入一个新节点,新节点数据为data + */ +void clist_push_front(clist_node_t **ppFirst, cnode data) +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + clist_node_t *node = CreateNode(data); + node->Next = *ppFirst; + *ppFirst = node; +} + +/** + * @brief 尾部删除 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @return void + * @note 删除链表中最后一个节点 + */ +void clist_pop_back(clist_node_t **ppFirst) // 尾部删除 +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + DBG_ASSERT(*ppFirst != NULL __DBG_LINE); + if ((*ppFirst)->Next == NULL) + { + osel_mem_free(*ppFirst); + *ppFirst = NULL; + return; + } + clist_node_t *p = *ppFirst; + while (p->Next->Next != NULL) + p = p->Next; + osel_mem_free(p->Next); + p->Next = NULL; +} + +/** + * @brief 头部删除 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @return void + * @note 删除链表中第一个节点 + */ +void clist_pop_front(clist_node_t **ppFirst) +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + DBG_ASSERT(*ppFirst != NULL __DBG_LINE); // 链表不是空链表 + clist_node_t *first = *ppFirst; + *ppFirst = (*ppFirst)->Next; + osel_mem_free(first); +} +/** + * @brief 按节点指针插入 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @param {clist_node_t} *pPos 要插入的节点位置 + * @param {cnode} data 要插入的节点数据 + * @return void + * @note 在指定节点位置插入一个新节点,新节点数据为data + */ +void clist_insert_for_node(clist_node_t **ppFirst, clist_node_t *pPos, cnode data) +{ + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + if (*ppFirst == pPos) + { + clist_push_front(ppFirst, data); + return; + } + clist_node_t *newNode = CreateNode(data); + clist_node_t *p; + + for (p = *ppFirst; p->Next != pPos; p = p->Next) + { + } // 找到pos前的一个节点 + p->Next = newNode; // 改变的是字段内的值,而不是指针的值 + newNode->Next = pPos; +} +/** + * @brief 按位置插入 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @param {int32_t} Pos 插入位置 + * @param {cnode} data 要插入的节点数据 + * @return {int32_t} 插入成功返回1,否则返回0 + * @note 在指定位置插入一个新节点,新节点数据为data + */ +int32_t clist_insert(clist_node_t **ppFirst, int32_t Pos, cnode data) // 按位置插入 +{ + clist_node_t *p = *ppFirst; + for (int32_t i = 0; i < Pos; i++) + { + if (p == NULL) + return 0; + p = p->Next; + } + clist_insert_for_node(ppFirst, p, data); + return 1; +} + +/** + * @brief 按位置删除 + * @param {clist_node_t} **ppFirst 指向链表头节点的指针 + * @param {int32_t} Pos 要删除的节点位置 + * @return {int32_t} 删除成功返回1,否则返回0 + * @note 从指定位置删除一个节点 + */ +int32_t cListErase(clist_node_t **ppFirst, int32_t Pos) +{ + clist_node_t *p = *ppFirst; + for (int32_t i = 0; i < Pos; i++) + { + if (p == NULL) + return 0; + p = p->Next; + } + clist_erase_for_node(ppFirst, p); + return 1; +} + +/** + * @brief 删除给定结点之后的所有结点 + * @param {clist_node_t **} ppFirst 指向链表头结点的指针 + * @param {clist_node_t *} pPos 要删除的结点 + * @return void + * @note + */ +void clist_erase_for_node(clist_node_t **ppFirst, clist_node_t *pPos) +{ + if (*ppFirst == pPos) + { + clist_pop_front(ppFirst); + return; + } + clist_node_t *p = *ppFirst; + while (p->Next != pPos) + p = p->Next; + p->Next = pPos->Next; + osel_mem_free(pPos); +} + +/** + * @brief 删除指定值的结点 + * @param {clist_node_t **} ppFirst 指向链表头结点的指针 + * @param {cnode} data 要删除的结点数据 + * @return void + * @note + */ +void clist_remove(clist_node_t **ppFirst, cnode data) +{ + clist_node_t *p = *ppFirst; + clist_node_t *prev = NULL; + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + if (*ppFirst == NULL) + return; + while (p) + { + if (p->data == data) + { + if (*ppFirst == p) // 删除的是第一个节点 + { + *ppFirst = p->Next; + osel_mem_free(p); + p = NULL; + } + else // 删除中间节点 + { + prev->Next = p->Next; + osel_mem_free(p); + p = NULL; + } + break; + } + prev = p; + p = p->Next; + } +} + +/** + * @brief 删除指定值的所有结点 + * @param {clist_node_t **} ppFirst 指向链表头结点的指针 + * @param {cnode} data 要删除的结点数据 + * @return void + * @note + */ +void clist_remove_all(clist_node_t **ppFirst, cnode data) +{ + clist_node_t *p = NULL; + clist_node_t *prev = NULL; + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + if (*ppFirst == NULL) + return; + p = *ppFirst; + while (p) + { + if (p->data == data) + { + if (*ppFirst == p) // 删除的是第一个节点 + { + *ppFirst = p->Next; + osel_mem_free(p); + p = *ppFirst; + } + else // 删除中间节点 + { + prev->Next = p->Next; + osel_mem_free(p); + p = prev; + } + } + prev = p; + p = p->Next; + } +} + +/** + * @brief 销毁链表,每个节点都要销毁 + * @param {clist_node_t **} ppFirst 指向链表头结点的指针 + * @return void + * @note + */ +void clist_destroy(clist_node_t **ppFirst) +{ + clist_node_t *p = NULL; + clist_node_t *del = NULL; + DBG_ASSERT(ppFirst != NULL __DBG_LINE); + p = *ppFirst; + while (p) + { + del = p; + p = p->Next; + osel_mem_free(del); + del = NULL; + } + *ppFirst = NULL; +} + +/** + * @brief 按值查找,返回第一个找到的结点指针,如果没找到,返回 NULL + * @param {clist_node_t *} pFirst 指向链表头结点的指针 + * @param {cnode} data 要查找的结点数据 + * @return {clist_node_t *} 找到的结点指针,如果没有找到,返回 NULL + * @note + */ +clist_node_t *clist_find(clist_node_t *pFirst, cnode data) +{ + for (clist_node_t *p = pFirst; p != NULL; p = p->Next) + { + if (p->data == data) + return p; + } + return NULL; +} diff --git a/lib/src/cmac.c b/lib/src/cmac.c new file mode 100644 index 0000000..3d7f81d --- /dev/null +++ b/lib/src/cmac.c @@ -0,0 +1,159 @@ +/************************************************************************** +Copyright (C) 2009 Lander Casado, Philippas Tsigas + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files +(the "Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimers. Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +In no event shall the authors or copyright holders be liable for any special, +incidental, indirect or consequential damages of any kind, or any damages +whatsoever resulting from loss of use, data or profits, whether or not +advised of the possibility of damage, and on any theory of liability, +arising out of or in connection with the use or performance of this software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS WITH THE SOFTWARE + +*****************************************************************************/ +// #include +// #include +#include +#include "aes.h" +#include "cmac.h" +#include "osel_arch.h" + +#define LSHIFT(v, r) \ + do \ + { \ + int32_t i; \ + for (i = 0; i < 15; i++) \ + (r)[i] = (v)[i] << 1 | (v)[i + 1] >> 7; \ + (r)[15] = (v)[15] << 1; \ + } while (0) + +#define XOR(v, r) \ + do \ + { \ + int32_t i; \ + for (i = 0; i < 16; i++) \ + { \ + (r)[i] = (r)[i] ^ (v)[i]; \ + } \ + } while (0) + +void AES_CMAC_Init(AES_CMAC_CTX *ctx) +{ + osel_memset(ctx->X, 0, sizeof ctx->X); + ctx->M_n = 0; + osel_memset(ctx->rijndael.ksch, '\0', 240); +} + +void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const uint8_t key[AES_CMAC_KEY_LENGTH]) +{ + // rijndael_set_key_enc_only(&ctx->rijndael, key, 128); + aes_set_key(key, AES_CMAC_KEY_LENGTH, &ctx->rijndael); +} + +void AES_CMAC_Update(AES_CMAC_CTX *ctx, const uint8_t *data, uint32_t len) +{ + uint32_t mlen; + uint8_t in[16]; + + if (ctx->M_n > 0) + { + mlen = MIN(16 - ctx->M_n, len); + osel_memcpy(ctx->M_last + ctx->M_n, data, mlen); + ctx->M_n += mlen; + if (ctx->M_n < 16 || len == mlen) + return; + XOR(ctx->M_last, ctx->X); + // rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + aes_encrypt(ctx->X, ctx->X, &ctx->rijndael); + data += mlen; + len -= mlen; + } + while (len > 16) /* not last block */ + { + + XOR(data, ctx->X); + // rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X); + + osel_memcpy(in, &ctx->X[0], 16); // Bestela ez du ondo iten + aes_encrypt(in, in, &ctx->rijndael); + osel_memcpy(&ctx->X[0], in, 16); + + data += 16; + len -= 16; + } + /* potential last block, save it */ + osel_memcpy(ctx->M_last, data, len); + ctx->M_n = len; +} + +void AES_CMAC_Final(uint8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx) +{ + uint8_t K[16]; + uint8_t in[16]; + /* generate subkey K1 */ + osel_memset(K, '\0', 16); + + // rijndael_encrypt(&ctx->rijndael, K, K); + + aes_encrypt(K, K, &ctx->rijndael); + + if (K[0] & 0x80) + { + LSHIFT(K, K); + K[15] ^= 0x87; + } + else + LSHIFT(K, K); + + if (ctx->M_n == 16) + { + /* last block was a complete block */ + XOR(K, ctx->M_last); + } + else + { + /* generate subkey K2 */ + if (K[0] & 0x80) + { + LSHIFT(K, K); + K[15] ^= 0x87; + } + else + LSHIFT(K, K); + + /* padding(M_last) */ + ctx->M_last[ctx->M_n] = 0x80; + while (++ctx->M_n < 16) + ctx->M_last[ctx->M_n] = 0; + + XOR(K, ctx->M_last); + } + XOR(ctx->M_last, ctx->X); + + // rijndael_encrypt(&ctx->rijndael, ctx->X, digest); + + osel_memcpy(in, &ctx->X[0], 16); // Bestela ez du ondo iten + aes_encrypt(in, digest, &ctx->rijndael); + osel_memset(K, 0, sizeof K); +} diff --git a/lib/src/cmd.c b/lib/src/cmd.c new file mode 100644 index 0000000..8a8a26d --- /dev/null +++ b/lib/src/cmd.c @@ -0,0 +1,111 @@ +/** + * @file cmd.c + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 命令解析器 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "cmd.h" + +#include + +static cmd_t *_cmd_begin, *_cmd_end; + +static int _cmd_to_lower(int c) +{ + if ((c >= 'A') && (c <= 'Z')) + return c + ('a' - 'A'); + return c; +} + +static unsigned int _cmd_hash(const char *str) +{ + int tmp, c = *str; + unsigned int seed = CMD_HASH; /* 'jiejie' string hash */ + unsigned int hash = 0; + + while (*str) + { + tmp = _cmd_to_lower(c); + hash = (hash ^ seed) + tmp; + str++; + c = *str; + } + return hash; +} + +static void _cmd_init(const void *begin, const void *end) +{ + _cmd_begin = (cmd_t *)begin; + _cmd_end = (cmd_t *)end; +} + +static cmd_t *_get_next_cmd(cmd_t *cmd) +{ + unsigned int *ptr; + ptr = (unsigned int *)(cmd + 1); + while ((*ptr == 0) && ((unsigned int *)ptr < (unsigned int *)_cmd_end)) + ptr++; + + return (cmd_t *)ptr; +} + +static int _cmd_match(const char *str, const char *cmd) +{ + int c1, c2; + + do + { + c1 = _cmd_to_lower(*str++); + c2 = _cmd_to_lower(*cmd++); + } while ((c1 == c2) && c1); + + return c1 - c2; +} + +static void _list(void) +{ + cmd_t *index; + for (index = _cmd_begin; index < _cmd_end; index = _get_next_cmd(index)) + { + // printf("%s -->%s\n", index->cmd, index->cmd_mess); + } +} +REGISTER_CMD(_list, _list, list all command); + +void cmd_init(void) +{ + cmd_t *index; + +#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM C Compiler */ + extern const int CMDS$$Base; + extern const int CMDS$$Limit; + _cmd_init(&CMDS$$Base, &CMDS$$Limit); +#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */ + _cmd_init(__section_begin("CMDS"), __section_end("CMDS")); +#endif + + for (index = _cmd_begin; index < _cmd_end; index = _get_next_cmd(index)) + { + index->hash = _cmd_hash(index->cmd); + } +} + +void cmd_parsing(char *str) +{ + cmd_t *index; + unsigned int hash = _cmd_hash(str); + + for (index = _cmd_begin; index < _cmd_end; index = _get_next_cmd(index)) + { + if (hash == index->hash) + { + if (_cmd_match(str, index->cmd) == 0) + { + index->handler(); + break; + } + } + } +} diff --git a/lib/src/data_analysis.c b/lib/src/data_analysis.c new file mode 100644 index 0000000..76993f4 --- /dev/null +++ b/lib/src/data_analysis.c @@ -0,0 +1,468 @@ +/** + * @file data_analysis.c + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 处理传输层的数据 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include +#include "../inc/data_analysis.h" +#include "../inc/sqqueue.h" +#include "../inc/debug.h" + +typedef uint8_t data_entry_t; +typedef void (*state_t)(uint8_t data_id, uint8_t sig, uint8_t ch); + +typedef enum _event /* enumeration */ +{ + SD_SIG, + LD_SIG, + CHAR_SIG, + ED_SIG, +} sig_event; + +typedef struct _fsm_t_ +{ + state_t current_state; +} fsm_t; + +typedef struct _DATA_frm_t_ +{ + uint8_t sd_index; + + struct + { + uint8_t data[DATA_LD_LEN_MAX]; + uint8_t index; + uint16_t frm_len; + } ld; // length describe + + uint16_t payload_len; // actually len of recvived data + uint8_t ed_index; + + uint16_t last_enter_q_num; // record the num of frames has entered the queue + uint8_t locked; + uint8_t sig; +} data_frm_t; + +#define TRAN(state) (fsm[data_id].current_state = (state_t)(state)) +#define FSM_DISPATCH(data_id, sig, ch) (fsm[data_id].current_state((data_id), (sig), (ch))) + +static fsm_t fsm[DATA_NUM] = {0}; + +static data_frm_t data_frm_array[DATA_NUM]; +static data_reg_t data_reg_array[DATA_NUM]; + +static sqqueue_ctrl_t data_recv_sqq[DATA_NUM]; + +static void wait_sd_state(uint8_t data_id, uint8_t sig, uint8_t ch); +/** + * @brief 加锁数据 + * @param {uint8_t} data_id + * @note + */ +void lock_data(uint8_t data_id) +{ + data_frm_array[data_id].locked = TRUE; // 设置数据帧结构体的锁定标志为TRUE + for (uint8_t i = 0; i < data_frm_array[data_id].last_enter_q_num; i++) // 遍历数据接收缓冲区,删除所有已入队项 + { + data_recv_sqq[data_id].revoke(&data_recv_sqq[data_id]); // 删除项 + } + + data_frm_array[data_id].last_enter_q_num = 0; // 重置最后进入队列的项数量 + data_frm_array[data_id].sd_index = 0; // 重置SD索引 + data_frm_array[data_id].ed_index = 0; // 重置ED索引 + data_frm_array[data_id].ld.frm_len = 0; // 重置数据帧长度 + data_frm_array[data_id].payload_len = 0; // 重置负载长度 + + TRAN(wait_sd_state); // 切换到等待SD状态 +} + +/** + * @brief 解锁数据 + * @param {uint8_t} data_id + * @note + */ +void unlock_data(uint8_t data_id) +{ + TRAN(wait_sd_state); // 切换到等待SD状态 + + data_frm_array[data_id].last_enter_q_num = 0; // 重置最后进入队列的项数量 + data_frm_array[data_id].sd_index = 0; // 重置SD索引 + data_frm_array[data_id].ed_index = 0; // 重置ED索引 + data_frm_array[data_id].ld.frm_len = 0; // 重置数据帧长度 + data_frm_array[data_id].payload_len = 0; // 重置负载长度 + + data_frm_array[data_id].locked = FALSE; // 设置数据帧结构体的锁定标志为FALSE +} + +/** + * @brief 处理结束状态 + * @param {uint8_t} data_id + * @param {uint8_t} sig + * @param {uint8_t} ch + * @note + */ +static void end_state_handle(uint8_t data_id, uint8_t sig, uint8_t ch) +{ + TRAN(wait_sd_state); // 切换到等待SD状态 + data_frm_array[data_id].ld.frm_len = 0; // 重置数据帧长度 + data_frm_array[data_id].payload_len = 0; // 重置负载长度 + data_frm_array[data_id].last_enter_q_num = 0; // 重置最后进入队列的项数量 + if (data_reg_array[data_id].func_ptr != NULL) // 如果数据项注册了处理函数 + { + (*(data_reg_array[data_id].func_ptr))(); // 调用处理函数 + } +} + +/** + * @brief 这个函数处理等待结束状态。 + * @param {uint8_t} data_id - 数据的ID。 + * @param {uint8_t} sig - 信号。 + * @param {uint8_t} ch - 通道。 + * @return {*} + * @note + */ +static void wait_end_state(uint8_t data_id, uint8_t sig, uint8_t ch) +{ + // 如果数据寄存器数组无效 + if (!data_reg_array[data_id].ed.valid) + { + // 如果数据帧数组的帧长度为0 + if (data_frm_array[data_id].ld.frm_len == 0) + { + // 转换到等待SD状态 + TRAN(wait_sd_state); + + // 处理结束状态 + end_state_handle(data_id, sig, ch); + + // 对于数据帧数组的最后一个进入队列的数量,撤销数据接收队列 + for (uint8_t i = 0; i < data_frm_array[data_id].last_enter_q_num; i++) + { + data_recv_sqq[data_id].revoke(&data_recv_sqq[data_id]); + } + // 将数据帧数组的最后一个进入队列的数量设置为0 + data_frm_array[data_id].last_enter_q_num = 0; + } + else + { + // 如果数据接收队列进入成功 + if (data_recv_sqq[data_id].enter(&data_recv_sqq[data_id], (void *)&ch)) + { + // 增加数据帧数组的最后一个进入队列的数量 + data_frm_array[data_id].last_enter_q_num++; + // 如果增加的数据帧数组的有效载荷长度等于数据帧数组的帧长度 + if (++data_frm_array[data_id].payload_len == + data_frm_array[data_id].ld.frm_len) + { + // 处理结束状态 + end_state_handle(data_id, sig, ch); + } + } + else + { + // 锁定数据 + lock_data(data_id); + } + } + } + else + { + // 如果数据帧数组的帧长度为0 + if (data_frm_array[data_id].ld.frm_len == 0) + { + // 如果数据接收队列进入成功 + if (data_recv_sqq[data_id].enter(&data_recv_sqq[data_id], (void *)&ch)) + { + // 增加数据帧数组的最后一个进入队列的数量 + data_frm_array[data_id].last_enter_q_num++; + // 如果数据寄存器数组的数据等于通道 + if (data_reg_array[data_id].ed.data[0] == ch) + { + // 处理结束状态 + end_state_handle(data_id, sig, ch); + } + } + else + { + // 锁定数据 + lock_data(data_id); + } + } + else + { + // 如果数据接收队列进入成功 + if (data_recv_sqq[data_id].enter(&data_recv_sqq[data_id], (void *)&ch)) + { + // 增加数据帧数组的最后一个进入队列的数量 + data_frm_array[data_id].last_enter_q_num++; + // 如果增加的数据帧数组的有效载荷长度大于等于数据帧数组的帧长度减去数据寄存器数组的位置 + if (++data_frm_array[data_id].payload_len >= + data_frm_array[data_id].ld.frm_len - data_reg_array[data_id].ld.pos) + { + // 如果数据寄存器数组的数据等于通道 + if (data_reg_array[data_id].ed.data[0] == ch) + { + // 处理结束状态 + end_state_handle(data_id, sig, ch); + } + } + } + else + { + // 锁定数据 + lock_data(data_id); + } + } + } +} + +/** + * @brief 处理等待LD状态 + * @param {uint8_t} data_id + * @param {uint8_t} sig + * @param {uint8_t} ch + * @return {*} + * @note + */ +static void wait_ld_state(uint8_t data_id, uint8_t sig, uint8_t ch) +{ + if (!data_reg_array[data_id].ld.valid) // 如果数据项未注册LD状态 + { + TRAN(wait_end_state); // 切换到等待结束状态 + FSM_DISPATCH(data_id, data_frm_array[data_id].sig, ch); // 调用FSM处理函数 + return; + } + data_frm_array[data_id].ld.data[data_frm_array[data_id].ld.index++] = ch; // 将字符添加到数据帧中 + if (data_recv_sqq[data_id].enter(&data_recv_sqq[data_id], (void *)&ch)) // 尝试进入队列 + { + data_frm_array[data_id].last_enter_q_num++; // 增加最后进入队列的项数量 + if (data_frm_array[data_id].ld.index == data_reg_array[data_id].ld.len) // 如果索引等于数据项长度 + { + if (data_reg_array[data_id].ld.little_endian == TRUE) // 如果小端存储 + { + data_frm_array[data_id].ld.frm_len = + data_frm_array[data_id].ld.data[DATA_LD_LEN_MAX - 1] * 256 + + data_frm_array[data_id].ld.data[DATA_LD_LEN_MAX - 2]; + } + else + { + data_frm_array[data_id].ld.frm_len = + data_frm_array[data_id].ld.data[DATA_LD_LEN_MAX - 2] * 256 + + data_frm_array[data_id].ld.data[DATA_LD_LEN_MAX - 1]; + } + + if (data_reg_array[data_id].ld.len == 1) // 如果是只有1个字节长度的数据 + { + data_frm_array[data_id].ld.frm_len = data_frm_array[data_id].ld.data[0]; + } + + if ((data_frm_array[data_id].ld.frm_len > data_reg_array[data_id].argu.len_max) || (data_frm_array[data_id].ld.frm_len < data_reg_array[data_id].argu.len_min)) + { + data_frm_array[data_id].ld.index = 0; + TRAN(wait_sd_state); // 切换到等待SD状态 + + for (uint8_t i = 0; i < data_frm_array[data_id].last_enter_q_num; i++) + { + data_recv_sqq[data_id].revoke(&data_recv_sqq[data_id]); // 删除项 + } + + data_frm_array[data_id].ld.frm_len = 0; + data_frm_array[data_id].last_enter_q_num = 0; + } + else + { + data_frm_array[data_id].ld.index = 0; + TRAN(wait_end_state); // 切换到等待结束状态 + } + } + } + else + { + lock_data(data_id); // 锁定数据 + } +} +/** + * @brief 等待SD状态处理函数 + * @param {uint8_t} data_id 数据ID + * @param {uint8_t} sig 信号 + * @param {uint8_t} ch 字符 + * @return {*} + * @note + */ +static void wait_sd_state(uint8_t data_id, uint8_t sig, uint8_t ch) +{ + // 如果数据寄存器中的SD数据无效 + if (!data_reg_array[data_id].sd.valid) + { + // transition to wait_ld_state状态 + TRAN(wait_ld_state); + // 调用数据帧数组中对应的数据帧处理函数 + FSM_DISPATCH(data_id, data_frm_array[data_id].sig, ch); + return; + } + // 如果数据寄存器中的SD数据中的当前字节等于输入的字节 + if (data_reg_array[data_id].sd.data[data_frm_array[data_id].sd_index++] == ch) + { + // 如果输入字符串成功进入队列 + if (data_recv_sqq[data_id].enter(&data_recv_sqq[data_id], (void *)&ch)) + { + // 更新数据帧数组中对应的数据帧的最后一个进入队列的队列号 + data_frm_array[data_id].last_enter_q_num++; + // 如果数据帧中的SD数据索引等于数据寄存器中的SD数据长度 + if (data_frm_array[data_id].sd_index == data_reg_array[data_id].sd.len) + { + // 更新数据帧中的SD数据索引为0 + data_frm_array[data_id].sd_index = 0; + // transition to wait_ld_state状态 + TRAN(wait_ld_state); + } + } + // 如果输入字符串无法进入队列 + else + { + // 锁定数据 + lock_data(data_id); + } + } + // 如果数据寄存器中的SD数据中的当前字节不等于输入的字节 + else + { + // 遍历并删除队列中的输入字符串 + for (uint8_t i = 0; i < data_frm_array[data_id].last_enter_q_num; i++) + { + data_recv_sqq[data_id].revoke(&data_recv_sqq[data_id]); + } + + // 更新数据帧中的SD数据索引为0 + data_frm_array[data_id].sd_index = 0; + // 更新数据帧中的最后一个进入队列的队列号为0 + data_frm_array[data_id].last_enter_q_num = 0; + } +} + +/** + * @brief 处理数据字符 + * @param {uint8_t} data_id + * @param {uint8_t} ch + * @return {*} + * @note + */ +static void data_char_handle(uint8_t data_id, uint8_t ch) +{ + // 如果数据ID对应的数据寄存器的回显使能标志为真 + if (data_reg_array[data_id].echo_en) + { + // 将输入字符写入数据寄存器 + data_write(data_id, &ch, 1); + } + + // 调用数据帧数组中对应的数据帧处理函数 + FSM_DISPATCH(data_id, data_frm_array[data_id].sig, ch); +} + +/** + * @brief 初始化数据帧处理机 + * @param {uint8_t} data_id + * @return {*} + * @note + */ +data_interupt_cb_t data_fsm_init(uint8_t data_id) +{ + TRAN(wait_sd_state); // 切换到等待SD状态 + data_reg_array[data_id].func_ptr = NULL; // 设置数据ID寄存器的回调函数为空 + memset(&data_frm_array[data_id], 0, sizeof(data_frm_t)); // 初始化数据帧结构体 + data_frm_array[data_id].sig = CHAR_SIG; // 设置数据帧签名 + + if (sqqueue_ctrl_init(&data_recv_sqq[data_id], + sizeof(data_entry_t), + DATA_BUF_RECV_SQQ_LEN) == FALSE) // 初始化数据接收缓冲区 + { + DBG_ASSERT(FALSE __DBG_LINE); // 如果初始化失败,输出调试信息 + } + + return data_char_handle; // 返回数据处理回调函数指针 +} +/** + * @brief 读取数据 + * @param {uint8_t} id + * @param {void} *buffer + * @param {uint16_t} len + * @return {*} + * @note + */ +uint8_t data_read(uint8_t id, void *buffer, uint16_t len) +{ + uint8_t i = 0; + data_entry_t e; + uint8_t *buf = (uint8_t *)buffer; + + // 如果接收队列中存在长度大于等于输入长度的数据项 + if (data_recv_sqq[id].get_len(&data_recv_sqq[id]) >= len) + { + // 遍历接收队列并读取数据项到缓冲区 + for (i = 0; i < len; i++) + { + e = *((data_entry_t *)data_recv_sqq[id].del(&data_recv_sqq[id])); + buf[i] = e; + } + } + // 如果接收队列中不存在长度大于等于输入长度的数据项 + else + { + // 遍历接收队列并读取数据项到缓冲区 + while ((data_recv_sqq[id].get_len(&data_recv_sqq[id]) != 0) && (i < len)) + { + e = *((data_entry_t *)data_recv_sqq[id].del(&data_recv_sqq[id])); + buf[i++] = e; + } + } + + // 如果数据帧数组中对应的数据帧被锁定 + if (data_frm_array[id].locked) + { + // 解锁数据 + unlock_data(id); + } + + return i; +} + +/** + * @brief + * @param {uint8_t} id + * @param {uint8_t} *string + * @param {uint16_t} len + * @return {*} + * @note + */ +void data_write(uint8_t id, uint8_t *const string, uint16_t len) +{ +} + +/** + * @brief 设置数据寄存器 + * @param {uint8_t} id + * @param {data_reg_t} reg + * @return {*} + * @note + */ +BOOL data_reg(uint8_t id, data_reg_t reg) +{ + if (data_reg_array[id].func_ptr == NULL) + { + memcpy((void *)&data_reg_array[id], (void *)®, sizeof(reg)); + return TRUE; + } + else + { + return FALSE; + } +} +void data_unreg(uint8_t id) +{ + memset((void *)&data_reg_array[id], 0, sizeof(data_reg_t)); + data_reg_array[id].func_ptr = NULL; +} diff --git a/lib/src/debug.c b/lib/src/debug.c new file mode 100644 index 0000000..72a4639 --- /dev/null +++ b/lib/src/debug.c @@ -0,0 +1,44 @@ +/* + * @File: debug.c + * @Descripttion: + * @Version: 1.0 + * @Author: + * @Date: 2022-12-10 20:15:01 + * @LastEditors: xxx + * @LastEditTime: 2023-08-08 14:39:18 + */ +#include "../inc/debug.h" + +#ifndef STM32 +BOOL DBG_ASSERT(uint8_t cond _DBG_LINE_) +{ + do + { + if ((cond) == FALSE) + { + LOG_ERR("DBG_ASSERT:%d", line); + return FALSE; + } + } while (__LINE__ == -1); + return TRUE; +} + +#else +#include "board.h" +#include "sys.h" +BOOL DBG_ASSERT(uint8_t cond _DBG_LINE_) +{ + do + { + if ((cond) == FALSE) + { +// dbg_assert_line = line; + while (1) + { + LOG_ERR("DBG_ASSERT:%d", line); + } + } + } while (__LINE__ == -1); + return TRUE; +} +#endif diff --git a/lib/src/filter.c b/lib/src/filter.c new file mode 100644 index 0000000..1a907ce --- /dev/null +++ b/lib/src/filter.c @@ -0,0 +1,156 @@ +#include "filter.h" +#include +#include "osel_arch.h" + +/** + * @brief 初始化卡尔曼滤波器 + * + * 初始化给定的卡尔曼滤波器配置结构体,并设置初始参数。 + * + * @param cfg 卡尔曼滤波器配置结构体指针 + * @param change_max 最大变化量 + */ +void kalman_init(kalman_t *cfg, float32 change_max) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + // 初始化卡尔曼滤波器配置 + cfg->x = 0; // 初始状态估计值 + cfg->p = 5; // 初始估计误差协方差 + cfg->a = 1; // 状态转移矩阵 + cfg->h = 1; // 观测矩阵 + cfg->q = 0.25; // 过程噪声协方差 + cfg->r = 1; // 观测噪声协方差 + cfg->change_max = change_max; // 最大变化值 +} + +/** + * @brief 卡尔曼滤波器更新函数 + * + * 根据输入的观测值和卡尔曼滤波器的配置参数,更新滤波器的状态和估计值。 + * + * @param cfg 卡尔曼滤波器配置指针 + * @param input 输入的观测值 + * + * @return 更新后的卡尔曼滤波器估计值 + */ +float32 kalman_update(kalman_t *cfg, float32 input) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + // 计算变化值 + float32 delta = input - cfg->x; + // 如果变化值超过0.5,直接返回输入值 + if (ABS(delta) > cfg->change_max) + { + cfg->x = input; + return cfg->x; + } + cfg->x = cfg->a * cfg->x; + cfg->p = cfg->a * cfg->a * cfg->p + cfg->q; + + cfg->gain = cfg->p * cfg->h / (cfg->p * cfg->h * cfg->h + cfg->r); + cfg->x = cfg->x + cfg->gain * (input - cfg->h * cfg->x); + cfg->p = (1 - cfg->gain * cfg->h) * cfg->p; + + return cfg->x; +} + +// 一阶滞后滤波法 +void lpf_init(lpf_t *cfg) +{ + cfg->fisrt_flag = TRUE; + cfg->last_value = 0; + if (cfg->alpha <= 0 || cfg->alpha > 1) + { + cfg->alpha = 0.3f; + } +} + +float32 lpf_update(lpf_t *cfg, float32 input) +{ + float32 out; + + /***************** 如果第一次进入,则给 out_last 赋值 ******************/ + if (TRUE == cfg->fisrt_flag) + { + cfg->fisrt_flag = FALSE; + cfg->last_value = input; + } + + /*************************** 一阶滤波 *********************************/ + out = cfg->alpha * input + (1 - cfg->alpha) * cfg->last_value; + cfg->last_value = out; + + return out; +} + +void lpf_reset(lpf_t *cfg) +{ + cfg->fisrt_flag = TRUE; +} + +/** + * 滑动平均窗口滤波 + */ +void lpf_window_init(lpf_window_t *cfg, uint16_t size) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + if (cfg->window != NULL) + { + osel_mem_free(cfg->window); + } + osel_memset((uint8_t *)cfg, 0, sizeof(lpf_window_t)); + cfg->size = size; + cfg->window = (float32 *)osel_mem_alloc(sizeof(float32) * size); + DBG_ASSERT(cfg->window != NULL __DBG_LINE); + cfg->index = 0; + cfg->sum = 0; +} + +void lpf_window_dinit(lpf_window_t *cfg) +{ + if (cfg != NULL) + { + if (cfg->window != NULL) + { + osel_mem_free(cfg->window); + } + osel_mem_free(cfg); + } +} + +// 滑动平均窗口重置 +void lpf_window_reset(lpf_window_t *cfg) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + cfg->index = 0; + cfg->sum = 0; + osel_memset((uint8_t *)cfg->window, 0, sizeof(float32) * cfg->size); +} + +float32 lpf_window_update(lpf_window_t *cfg, float32 input) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + cfg->sum = 0; + // 如果窗口未满,直接添加新值到当前索引位置 + if (cfg->index < cfg->size) + { + cfg->window[cfg->index++] = input; + } + else + { + // 如果窗口已满,替换最旧的值 + for (uint16_t i = 0; i < cfg->size - 1; i++) + { + cfg->window[i] = cfg->window[i + 1]; + } + cfg->window[cfg->size - 1] = input; + } + // 计算窗口中所有值的和 + for (uint16_t i = 0; i < cfg->index; i++) + { + cfg->sum += cfg->window[i]; + } + // 计算平均值 + cfg->out = cfg->sum / cfg->index; + return cfg->out; +} diff --git a/lib/src/lib.c b/lib/src/lib.c new file mode 100644 index 0000000..640bbe3 --- /dev/null +++ b/lib/src/lib.c @@ -0,0 +1,826 @@ +/* + * @Author: + * @day: 2023-04-11 08:21:19 + * @LastEditors: xxx + * @LastEditTime: 2023-08-15 10:14:58 + * @Description: + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "../inc/lib.h" +#include +#include +#include "cmac.h" +const uint8_t _days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +const uint16_t _month_days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; +static uint32_t crc32_table[256]; // CRC32表 + +// ASSIC码数组转字符串 +void assic_to_str(uint8_t *assic, uint8_t len, uint8_t *str) +{ + for (uint8_t i = 0; i < len; i++) + { + if (assic[i] == 0) + { + str[i] = 48; + } + else + { + str[i] = (char)assic[i]; + } + } +} + +// 每隔三位数加逗号,将数字转为每隔3位整数由逗号“,”分隔的字符串 +void add_commas(uint32_t num, char *result) +{ + char temp[64]; + sprintf(temp, "%d", num); + + int32_t len = strlen(temp); + int32_t commas = (len - 1) / 3; + + int32_t index = 0; + int32_t resultIndex = 0; + for (int32_t i = len - 1; i >= 0; i--) + { + result[resultIndex++] = temp[i]; + index++; + if (index % 3 == 0 && commas > 0) + { + result[resultIndex++] = ','; + commas--; + } + } + result[resultIndex] = '\0'; + + // Reverse the result string + int32_t start = 0; + int32_t end = resultIndex - 1; + while (start < end) + { + char temp = result[start]; + result[start] = result[end]; + result[end] = temp; + start++; + end--; + } +} + +void get_cpu_id(uint32_t *id) +{ +#ifdef STM32 + // 获取CPU唯一的ID + id[0] = *(uint32_t *)(UID_BASE); + id[1] = *(uint32_t *)(UID_BASE + 4); + id[2] = *(uint32_t *)(UID_BASE + 8); +#endif +} + +uint32_t cpu_encrypt(void) +{ + uint32_t cpuid[3]; +#ifdef STM32 + // 获取CPU唯一的ID + cpuid[0] = *(uint32_t *)(UID_BASE); + cpuid[1] = *(uint32_t *)(UID_BASE + 4); + cpuid[2] = *(uint32_t *)(UID_BASE + 8); +#endif + // 加密算法,很简单的加密算法 + uint32_t encrypt_code = (cpuid[0] >> 3) + (cpuid[1] >> 1) + (cpuid[2] >> 2); + return encrypt_code; +} + +// 判断加密 +BOOL cpu_judge_encrypt(uint32_t cupid_encrypt) +{ + uint32_t cpuid[4]; +#ifdef STM32 + // 获取CPU唯一的ID + cpuid[0] = *(uint32_t *)(UID_BASE); + cpuid[1] = *(uint32_t *)(UID_BASE + 4); + cpuid[2] = *(uint32_t *)(UID_BASE + 8); +#endif + // 加密算法,很简单的加密算法 + cpuid[3] = (cpuid[0] >> 3) + (cpuid[1] >> 1) + (cpuid[2] >> 2); + // 检查Flash中的UID是否合法 + return (cupid_encrypt == cpuid[3]); +} + +void convert_seconds(uint32_t total_seconds, char *date) +{ + uint32_t days = total_seconds / 86400; + uint32_t hours = (total_seconds % 86400) / 3600; + uint32_t minutes = (total_seconds % 3600) / 60; + uint32_t seconds = total_seconds % 60; + if (days > 0) + { + if (days > 1000) + { + sprintf(date, "%02d d %02d h", days, hours); + } + else + { + sprintf(date, "%02d d %02d h %02d m", days, hours, minutes); + } + } + else if (hours > 0) + { + sprintf(date, "%02d h %02d m %02d s", hours, minutes, seconds); + } + else if (minutes > 0) + { + sprintf(date, "%02d m %02d s", minutes, seconds); + } + else + { + sprintf(date, "%02d s", seconds); + } +} + +/** + * @brief Generate the CRC32 lookup table. + * + * This function generates the CRC32 lookup table used for fast computation of the + * CRC32 checksum. The table is generated using the polynomial 0xEDB88320, which is a + * common polynomial used in CRC32 calculations. + */ +static void generate_crc32_table() +{ + static BOOL is_init = FALSE; + if (is_init) + { + return; + } + uint32_t polynomial = 0xEDB88320; + for (uint32_t i = 0; i < 256; i++) + { + uint32_t crc = i; + for (uint32_t j = 0; j < 8; j++) + { + crc = (crc & 1) ? (crc >> 1) ^ polynomial : crc >> 1; + } + crc32_table[i] = crc; + } + is_init = TRUE; +} + +/** + * @brief 版本号1.0拆解成1和0 + * @param {uint8_t} *version_str + * @param {uint8_t} *hi + * @param {uint8_t} *lo + * @return {*} + */ +void version_split(uint8_t *version_str, uint8_t *hi, uint8_t *lo) +{ + uint8_t flag = 1; + + for (uint8_t i = 0; version_str[i] != '\0'; i++) + { + if (version_str[i] == '.') + { + flag = 0; + continue; + } + + if (flag) + { + *hi = *hi * 10 + (version_str[i] - '0'); + } + else + { + *lo = *lo * 10 + (version_str[i] - '0'); + } + } +} + +// 反序数组 +void reverse(uint8_t *buf, uint16_t len) +{ + uint8_t tmp; + uint16_t i; + for (i = 0; i < len / 2; i++) + { + tmp = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = tmp; + } +} + +/*** + * @brief 判断是否在数组中 + * @param {uint8_t} *arr 数组 + * @param {uint8_t} len 数组长度 + * @param {uint8_t} val 要判断的值 + * @return {*} TRUE: 在数组中 + */ +BOOL is_in_array(uint16_t *arr, uint16_t len, uint16_t val) +{ + uint16_t i; + for (i = 0; i < len; i++) + { + if (arr[i] == val) + { + return TRUE; + } + } + return FALSE; +} + +/** + * 计算并返回指定数据区域crc的值 + * + * @param data: 待计算的数据区首地址 + * @param length: 待计算的数据区长度 + * + * @return crc计算的结果 + */ +uint16_t crc16_compute(const uint8_t *const data, uint16_t length) +{ + uint16_t crcVal = 0xffff; + const uint8_t *ptr = data; + + for (uint16_t i = 0; i < length; i++) + { + crcVal ^= (uint16_t)*ptr++; + + for (uint8_t j = 0; j < 8; j++) + { + if (crcVal & 0x0001) + { + crcVal = (crcVal >> 1) ^ 0x8401; + } + else + { + crcVal >>= 1; + } + } + } + + return crcVal; +} + +/** + * @brief Calculate the CRC32 value of a data buffer. + * + * This function calculates the CRC32 value of a data buffer using the lookup table method. + * The lookup table is generated using the polynomial 0xEDB88320, which is a common polynomial used in CRC32 calculations. + * + * @param data The data buffer to calculate the CRC32 value of. + * @param length The length of the data buffer in bytes. + * @return The CRC32 value of the data buffer. + */ +uint32_t crc32_compute(const uint8_t *const data, uint16_t length) +{ + generate_crc32_table(); + uint32_t crc = 0xFFFFFFFF; + for (size_t i = 0; i < length; i++) + { + crc = (crc >> 8) ^ crc32_table[(crc ^ data[i]) & 0xFF]; + } + return crc ^ 0xFFFFFFFF; +} + +/** + * @brief 计算 64 位 CRC 值 + * + * 根据给定的数据块和长度,使用 AES-CMAC 算法和 CRC32 算法计算 64 位 CRC 值。 + * 使用这个函数heap设置0x800 + * + * @param data 数据块指针 + * @param length 数据块长度 + * + * @return 返回计算得到的 64 位 CRC 值 + */ +uint64_t crc64_compute(const uint8_t *const data, const uint16_t length) +{ + uint8_t cmac_key[] = { + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥 + uint8_t dst[4]; + uint8_t mic[16]; + uint8_t *p; + uint32_t lo = 0, hi = 0; + AES_CMAC_CTX AesCmacCtx[1]; // 密钥扩展表 + AES_CMAC_Init(AesCmacCtx); // 完成密钥扩展表的初始化 + AES_CMAC_SetKey(AesCmacCtx, cmac_key); // 完成密钥扩展表数据 // 存放生成校验数据的数组 + AES_CMAC_Update(AesCmacCtx, data, length & 0xFF); // 完成数据的奇偶校验 + AES_CMAC_Final(mic, AesCmacCtx); // 生成16个字节的校验表 + uint32_t xor_vol = (uint32_t)((uint32_t)mic[3] << 24 | (uint32_t)mic[2] << 16 | (uint32_t)mic[1] << 8 | (uint32_t)mic[0]); // 取表4个字节作为校验码 + p = (uint8_t *)&xor_vol; + osel_memcpy(dst, p, 4); + lo = (uint32_t)dst[0] << 24 | (uint32_t)dst[1] << 16 | (uint32_t)dst[2] << 8 | (uint32_t)dst[3]; + hi = crc32_compute(data, length); + return ((uint64_t)hi << 32) | lo; +} + +/** + * 计算并返回指定数据区域异或的值 + * + * @param data: 待计算的数据区首地址 + * @param length: 待计算的数据区长度 + * + * @return 异或计算的结果 + */ +uint8_t xor_compute(const uint8_t *const data, uint16_t length) +{ + uint16_t i; + const uint8_t *ptr = data; + uint8_t xor = 0; + for (i = 0; i < length; i++) + { + xor ^= *ptr; + ptr++; + } + return xor; +} + +// 通过bit位获取置1个数量 +uint8_t get_bit_num(uint8_t bit) +{ + uint8_t num = 0; + while (bit) + { + if (bit & 0x01) + { + num++; + } + bit >>= 1; + } + return num; +} + +// 通过bit位获取置1的位置 +BOOL is_bit_set(int32_t x, int32_t k) +{ + int32_t mask = 1 << k; + return (x & mask) != 0; +} + +// 判断数组是否全是同一个值 +BOOL is_same_value(uint8_t *buf, uint16_t len, uint8_t value) +{ + uint16_t i; + for (i = 0; i < len; i++) + { + if (buf[i] != value) + { + return FALSE; + } + } + return TRUE; +} + +// 检查是否是闰年 +uint8_t is_leap_year(uint16_t year) +{ + return (year % 400 == 0) || (year % 100 != 0 && year % 4 == 0); +} + +// 检查日期是否合法 +BOOL is_valid_date(uint8_t year, uint8_t month, uint8_t day) +{ + if (year < 1 || month < 1 || month > 12 || day < 1) + { + return false; + } + + uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + // 如果是闰年,2月有29天 + if (is_leap_year(year)) + { + days_in_month[1] = 29; + } + + return day <= days_in_month[month - 1]; +} + +// 检查时间是否合法 +BOOL is_valid_time(uint8_t hour, uint8_t minute, uint8_t second) +{ + return (hour < 24) && + (minute < 60) && + (second < 60); +} + +// 检查日期和时间是否合法 +BOOL is_valid_datetime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) +{ + return is_valid_date(year, month, day) && is_valid_time(hour, minute, second); +} + +// 计算从1970年1月1日到给定年月日的天数 +uint32_t days_since_1970(uint16_t year, uint8_t month, uint8_t day) +{ + static const uint8_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + uint32_t days = 0; + uint16_t y = 1970; + + // 计算整年过去的天数 + while (y < year) + { + days += is_leap_year(y) ? 366 : 365; + y++; + } + + // 计算当前年份中过去的天数 + for (int32_t m = 0; m < month - 1; m++) + { + days += month_days[m]; + if (m == 1 && is_leap_year(year)) + { // 如果是2月且是闰年,多加一天 + days++; + } + } + + // 加上当前月的天数 + days += day - 1; // 因为day是从1开始的,而我们需要的是从0开始的偏移量 + + return days; +} + +// 将日期转换为秒数 +uint32_t date_to_seconds(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) +{ + uint32_t secs = 0; + uint32_t days = days_since_1970(year, month, day); + secs = days * 24 * 60 * 60; // 一天有24小时,每小时有60分钟,每分钟有60秒 + secs += hour * 60 * 60 + minute * 60 + second; + return secs; +} + +// 函数用于将秒数转换为日期和时间,年份4位 +void seconds_to_date(uint32_t total_seconds, rtc_date_t *date, rtc_time_t *time) +{ + uint16_t year = 1970; // Unix时间戳的起始年份 + uint8_t month = 1; + uint8_t day = 1; + uint32_t seconds = total_seconds; + uint8_t hours, minutes, secs; + + // 正确处理小时、分钟和秒 + hours = (seconds / 3600) % 24; + minutes = (seconds / 60) % 60; + secs = seconds % 60; + + // 计算日期 + uint32_t days = total_seconds / (24 * 3600); + seconds = total_seconds % (24 * 3600); // 更新seconds为剩余秒数 + + for (; days > 0; days--) + { + // 当前月份的天数 + uint8_t days_in_month = 31; + if (month == 2) + { + days_in_month = is_leap_year(year) ? 29 : 28; + } + else if (month == 4 || month == 6 || month == 9 || month == 11) + { + days_in_month = 30; + } + + if (++day > days_in_month) + { + day = 1; + if (++month > 12) + { + month = 1; + year++; + } + } + } + + // 将结果存储到结构体中 + date->year = year; + date->month = month; + date->day = day; + time->hour = hours; + time->minute = minutes; + time->second = secs; +} + +// 计算一年中的第几天 +uint16_t dayOfyear(uint16_t year, uint8_t month, uint8_t day) +{ + uint8_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + uint16_t total; + total = day; + if (month > 2 && is_leap_year(year)) + total += 1; + for (uint8_t i = 0; i < month - 1; i++) + { + total += month_days[i]; + } + return total; +} + +// 计算一年中的第几周 +uint16_t weekOfyear(uint16_t year, uint8_t month, uint8_t day) +{ + uint16_t day_of_year = dayOfyear(year, month, day); + return (day_of_year - 1) / 7 + 1; +} + +// 获取今天星期几 +uint8_t get_weekday(uint16_t year, uint8_t month, uint8_t day) +{ + uint8_t w = 0; + if (month == 1 || month == 2) + { + month += 12; + year--; + } + w = (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7; + return w + 1; +} + +// 传入十六进制0x23 返回十进制23 +uint8_t hex_format_dec(uint8_t hex) +{ + char buf[4]; + osel_memset((uint8_t *)buf, 0, 4); + sprintf(buf, "%x", hex); + int32_t dec = 0; + int32_t weight = 1; + int32_t len = strlen(buf); + for (int32_t i = len - 1; i >= 0; i--) + { + if (buf[i] >= '0' && buf[i] <= '9') + { + dec += (buf[i] - '0') * weight; + } + else if (buf[i] >= 'A' && buf[i] <= 'F') + { + dec += (buf[i] - 'A' + 10) * weight; + } + else if (buf[i] >= 'a' && buf[i] <= 'f') + { + dec += (buf[i] - 'a' + 10) * weight; + } + weight *= 10; + } + return dec; +} + +// 传入十进制23 返回十六进制0x23 +uint8_t dec_format_hex(uint8_t dec) +{ + char buf[4]; + osel_memset((uint8_t *)buf, 0, 4); + sprintf(buf, "%d", dec); + uint8_t hex = 0; + uint8_t len = strlen(buf); + for (uint8_t i = 0; i < len; i++) + { + char c = buf[i]; + if (c >= '0' && c <= '9') + { + hex = hex * 16 + (c - '0'); + } + else + { + continue; + } + } + return hex; +} + +/** + * @brief 日期时间转时间戳 + * @param {rtc_date_t} date + * @param {rtc_time_t} time + * @return {*} + * @note 年份是从2000年开始的 + */ +uint32_t time2stamp(const rtc_date_t *const date, const rtc_time_t *const time) +{ + uint32_t result; + uint16_t year = date->year + 2000; + // (time->hour - 0) * 3600 中的0改成8是北京时间的时区 + result = (year - 1970) * 365 * 24 * 3600 + (_month_days[date->month - 1] + date->day - 1) * 24 * 3600 + (time->hour - 0) * 3600 + time->minute * 60 + time->second; + result += (date->month > 2 && (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)) * 24 * 3600; // 闰月 + year -= 1969; + result += (year / 4 - year / 100 + year / 400) * 24 * 3600; // 闰年 + return result; +} + +/** + * @brief 时间戳转日期时间 + * @param {uint32_t} stamp + * @param {rtc_date_t} *date + * @param {rtc_time_t} *time + * @return {*} + * @note + */ +void stamp2time(uint32_t stamp, rtc_date_t *date, rtc_time_t *time) +{ + uint32_t days; + uint16_t leap_num; + + time->second = stamp % 60; + stamp /= 60; // 获取分 + time->minute = stamp % 60; + // stamp += 8 * 60; // 不需要加8小时 + stamp /= 60; // 获取小时 + time->hour = stamp % 24; + days = stamp / 24; + leap_num = (days + 365) / 1461; + if (((days + 366) % 1461) == 0) + { + date->year = (days / 366) + 1970 - 2000; + date->month = 12; + date->day = 31; + } + else + { + days -= leap_num; + date->year = (days / 365) + 1970 - 2000; + days %= 365; + days += 1; + if (((date->year % 4) == 0) && (days == 60)) + { + date->month = 2; + date->day = 29; + } + else + { + if (((date->year % 4) == 0) && (days > 60)) + --days; + for (date->month = 0; _days[date->month] < days; date->month++) + { + days -= _days[date->month]; + } + ++date->month; + date->day = days; + } + } +} + +/**************************排序**************************/ +static void swap(uint16_t *a, uint16_t *b) +{ + uint16_t t = *a; + *a = *b; + *b = t; +} + +static int32_t partition(uint16_t arr[], int32_t low, int32_t high) +{ + uint16_t pivot = arr[high]; + int32_t i = (low - 1); + for (int32_t j = low; j <= high - 1; j++) + { + if (arr[j] < pivot) + { + i++; + swap(&arr[i], &arr[j]); + } + } + swap(&arr[i + 1], &arr[high]); + return (i + 1); +} + +// 快速排序 +void quicksort(uint16_t arr[], int32_t low, int32_t high) +{ + if (low < high) + { + int32_t pi = partition(arr, low, high); + quicksort(arr, low, pi - 1); + quicksort(arr, pi + 1, high); + } +} + +// 插入排序 数据集小的时候效率高 +void insertion_sort(uint16_t arr[], uint16_t n) +{ + uint16_t i, j; + uint16_t key; + for (i = 1; i < n; i++) + { + key = arr[i]; + j = i; + + // 将大于key的元素向后移动一个位置 + while (j > 0 && arr[j - 1] > key) + { + arr[j] = arr[j - 1]; + j = j - 1; + } + arr[j] = key; + } +} + +/**************************线性拟合**************************/ +/** + * @brief 计算平均值 + * + * 根据给定的点集和指示标志,计算 X 或 Y 坐标的平均值。 + * + * @param points 点集指针 + * @param count 点集数量 + * @param isX 是否计算 X 坐标的平均值,为 1 则计算 X 坐标的平均值,为 0 则计算 Y 坐标的平均值 + * + * @return 返回计算得到的平均值 + */ +static float32 average(const point_t *points, int32_t count, int32_t isX) +{ + float32 sum = 0.0f; + for (int32_t i = 0; i < count; ++i) + { + sum += isX ? points[i].x : points[i].y; + } + return sum / count; +} + +/** + * @brief 计算线性回归函数值 + * + * 根据给定的 x 值和线性函数参数,计算并返回线性回归函数的值。 y = a * x + b + * + * @param x 输入的 x 值 + * @param param 线性函数参数结构体,包含斜率 a 和截距 b + * + * @return 线性回归函数的值 + */ +static float32 calculate_linear_regression_func(float32 x, linear_func_param_t param) +{ + return param.a * x + param.b; +} + +/** + * @brief 计算线性回归参数 + * + * 根据给定的数据点集合,计算线性回归的参数。 + * + * @param points 数据点集合指针 + * @param count 数据点数量 + * + * @return 线性回归参数结构体 + * + * @note 如果数据点数量少于2,则会输出错误日志并断言失败 + */ +linear_func_param_t calculate_linear_regression(const point_t *points, int32_t count) +{ + if (count < 2) + { + LOG_PRINT("参数points至少需要两组数据\n"); + DBG_ASSERT(FALSE __DBG_LINE); + } + + float32 xAverage = average(points, count, 1); + float32 yAverage = average(points, count, 0); + float32 sumXY = 0.0, sumXX = 0.0; + + for (int32_t i = 0; i < count; ++i) + { + float32 dX = points[i].x - xAverage; + float32 dY = points[i].y - yAverage; + sumXY += dX * dY; + sumXX += dX * dX; + } + + linear_func_param_t result; + result.a = sumXY / sumXX; + result.b = yAverage - result.a * xAverage; + return result; +} + +// 求线性度 公式 dYmax / Y * 100% +float32 get_linearity_value(const point_t *points, int32_t count, linear_func_param_t param) +{ + float32 dYMax = 0.0f; + for (int32_t i = 0; i < count; ++i) + { + float32 y = calculate_linear_regression_func(points[i].x, param); + float32 dY = ABS(y - points[i].y); + if (dY > dYMax) + dYMax = dY; + } + + float32 yFirst = calculate_linear_regression_func(points[0].x, param); + float32 yLast = calculate_linear_regression_func(points[count - 1].x, param); + return dYMax / ABS(yLast - yFirst) * 100; +} + +/** + // 示例使用 +int main() +{ + point_t points[] = {{1, 2}, {2, 3}, {3, 4}}; // 示例点数组 + int count = sizeof(points) / sizeof(points[0]); + linear_func_param_t param = calculate_linear_regression(points, count); + printf("Linear Regression: y = %f * x + %f\n", param.a, param.b); + + double linearity = get_linearity_value(points, count, param); + printf("Linearity Value: %f%%\n", linearity); + + return 0; +} + */ + +/**************************线性拟合**************************/ diff --git a/lib/src/malloc.c b/lib/src/malloc.c new file mode 100644 index 0000000..6c35d8f --- /dev/null +++ b/lib/src/malloc.c @@ -0,0 +1,338 @@ +/* + * @Author: + * @Date: 2023-04-11 08:50:25 + * @LastEditors: xxx + * @LastEditTime: 2023-06-15 10:23:12 + * @Description: + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ +#include "../inc/data_type_def.h" +#include "../inc/malloc.h" + +/***************************************************************************** +* 科普C语言 * +****************************************************************************** +*__align 作用 :对齐跟数据在内存中的位置有关,也就是内存字节对齐。有点 +* 难理解,需要通过以下举例来理解 +*__attribute__作用:可以设置函数属性、变量属性和类型属性。在这里我们用来绝 +* 对定位地址,即专门指点内存地址 +* +*实例一: +* __align(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000))); +* 意思:定义一个数组,数组大小为MEM2_MAX_SIZE,数组所占的空间能被32整除,其数组 +* 的起始内存地址为0X68000000。 +* 如果MEM2_MAX_SIZE为2,则数组内存空间大小为32字节;如果MEM2_MAX_SIZE为33,则数 +* 组内存空间大小为64字节。 +* +*实例二: +struct A{ + char a; + unsigned int b; + unsigned char c; + char d; +}; + +另一个结构体是: +structB{ + char a; + unsigned int b; + unsigned char c; + char d; +}__attribute__((align(8))); + +sizeof(A) = 12(内存空间大小12个字节) +sizeof(B) = 8(内存空间大小8个字节) +********************************************************************************/ +// 内存池(32字节对齐) +// 可控制的内存大小 +__attribute__((aligned(32))) uint8_t mem1base[MEM1_MAX_SIZE]; // 内部SRAM内存池 +// __attribute__((aligned(32))) uint8_t mem2base[MEM2_MAX_SIZE]; +__attribute__((aligned(32))) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0x2001C000))); // 外部SRAM内存池 +// 内存管理表 +// 可控制的内存控制块个数(每个内存块大小为32字节) +uint16_t mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; // 内部SRAM内存池MAP +uint16_t mem2mapbase[MEM2_ALLOC_TABLE_SIZE]; +// uint16_t mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000 + MEM2_MAX_SIZE))); // 外部SRAM内存池MAP + +// 内存管理参数 +// const 定义的变量的值是不允许改变的 +// 因为有内部SRAM和外部SRAM,所以用数组 +const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE, MEM2_ALLOC_TABLE_SIZE}; // 内存表大小(即控制多少内存块) +const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE}; // 内存分块大小(一块内存块占多少字节内存空间) +const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE, MEM2_MAX_SIZE}; // 内存池大小(即可以分配的内存空间大小) + +// 内存管理控制器 +struct _m_mallco_dev mallco_dev = + { + my_mem_init, // 内存初始化 + my_mem_perused, // 内存使用率 + mem1base, mem2base, // 内存池 + mem1mapbase, mem2mapbase, // 内存管理状态表 + 0, 0, // 内存管理未就绪 +}; + +/******************************************* + *函数功能 :复制内存里面的数据(从一个内存空间里拷贝数据到另一内存空间里) + *函数名 :mymemcpy + *函数参数 :void *des void *src uint32_t n + *函数返回值:void + *描述 : + * *des :目标地址 + * *src :源地址 + * n :要复制的长度(字节为单位) + *********************************************/ +void mymemcpy(void *des, void *src, uint32_t n) +{ + // 一般我们不会对要操作的参数指针进行操作 + // 而是通过一个变量指针作为中介,这样是为了出于安全保证 + uint8_t *xdes = des; + uint8_t *xsrc = src; + // 变量在++之前,则先用,后++ + // 变量在++之后,先++,后使用 + while (n--) + *xdes++ = *xsrc++; +} + +/***************************************************** + *函数功能 :设置内存(设置内存空间的值,一般用来对空间清0) + *函数名 :mymemset + *函数参数 :void *s uint8_t c uint32_t count + *函数返回值:void + *描述 : + * *s :内存首地址 + * c :要设置的值 + * count :需要设置的内存大小(字节为单位) + ******************************************************/ +void mymemset(void *s, uint8_t c, uint32_t count) +{ + // 一般我们不会对要操作的参数指针进行操作 + // 而是通过一个变量指针作为中介,这样是为了出于安全保证 + uint8_t *xs = s; + // 变量在++之前,则先用,后++ + // 变量在++之后,先++,后使用 + while (count--) + *xs++ = c; +} + +/***************************************************************** + *函数功能 :内存管理初始化 + *函数名 :my_mem_init + *函数参数 :uint8_t memx + *函数返回值:void + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * + * 其实所谓的初始化就是把内存池和内存表(他们的本质就是数组)清0 + ******************************************************************/ +void my_mem_init(uint8_t memx) +{ + mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx] * 2); // 内存状态表数据清零 + mymemset(mallco_dev.membase[memx], 0, memsize[memx]); // 内存池所有数据清零 + mallco_dev.memrdy[memx] = 1; // 内存管理初始化OK,即内存池和内存表都清0了 +} + +/***************************************************************** + *函数功能 :获取内存使用率 + *函数名 :my_mem_perused + *函数参数 :uint8_t memx + *函数返回值:void + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * + * 是否占用是通过判断mem1mapbase或mem2mapbase的数组成员是否非0,如果 + * 非0则被占用,之中数组成员值有一定意义,代表占了多少块,如值为10,则表示 + * 该申请了连续10个内存块 + ******************************************************************/ +uint8_t my_mem_perused(uint8_t memx) +{ + uint32_t used = 0; + uint32_t i; + // memtblsize:内存表大小(一共内存块数) + // 遍历内存表数组 + for (i = 0; i < memtblsize[memx]; i++) + { + // mallco_dev.memmap[memx]:内存表数组 + // 取出每个成员判断是否非0 + // 非0则是用了 + if (mallco_dev.memmap[memx][i]) + used++; + } + + // 使用数量/数量总数*100 = 使用率 + return (used * 100) / (memtblsize[memx]); +} + +/***************************************************************** + *函数功能 :内存分配(内部调用)------确定在内存池的偏移量 + *函数名 :my_mem_malloc + *函数参数 :uint8_t memx,uint32_t size + *函数返回值:uint32_t + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * size:要分配的内存大小(字节) + * 返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址 + * + * 注意:内存表的遍历是从后往前的 + ******************************************************************/ +uint32_t my_mem_malloc(uint8_t memx, uint32_t size) +{ + signed long offset = 0; // 偏移量变量 + uint32_t nmemb; // 需要的内存块数 + uint32_t cmemb = 0; // 连续空内存块数,保证我们申请的内存块是连续的 + uint32_t i; + // 判断是否已执行了初始化 + if (!mallco_dev.memrdy[memx]) + mallco_dev.init(memx); // 未初始化,先执行初始化 + if (size == 0) + return 0XFFFFFFFF; // 不需要分配 + // 内存块数 = 申请空间大小(字节单位) / t一个内存块大小(字节单位) + nmemb = size / memblksize[memx]; // 获取需要分配的连续内存块数 + // 申请空间大小(字节单位) / t一个内存块大小(字节单位) != 0 + // 如果非0则要多申请一块内存块 + if (size % memblksize[memx]) + nmemb++; + // 内存表的遍历是从后往前的 + for (offset = memtblsize[memx] - 1; offset >= 0; offset--) // 搜索整个内存控制区 + { + // 判断该内存块是否被占用了 + if (!mallco_dev.memmap[memx][offset]) + cmemb++; // 连续空内存块数增加 + // 保证内存块的连续性 + else + cmemb = 0; // 连续内存块清零 + // 确定好所有内存块位置后 + if (cmemb == nmemb) // 找到了连续nmemb个空内存块 + { + for (i = 0; i < nmemb; i++) // 标注内存块非空 + { + // 开始往内存块在内存表数组的位置标记该内存块被占用 + mallco_dev.memmap[memx][offset + i] = nmemb; + } + + // 确定申请空间在内存池数组位置 在内存表数组位置*一个内存块大小(32字节) + return (offset * memblksize[memx]); // 返回偏移地址 + } + } + return 0XFFFFFFFF; // 未找到符合分配条件的内存块 +} + +/***************************************************************** + *函数功能 :释放内存(内部调用)------内存池偏移量清除申请空间在内存表的占用标志 + *函数名 :my_mem_free + *函数参数 :uint8_t memx,uint32_t offset + *函数返回值:uint32_t + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * size:内存地址偏移(字节)--------也就是在内存池数组的位置 + * 返回值:0,释放成功;1,释放失败; + * + ******************************************************************/ +uint8_t my_mem_free(uint8_t memx, uint32_t offset) +{ + int i; + int index; + int nmemb; + + // 判断是否初始化 + if (!mallco_dev.memrdy[memx]) // 未初始化,先执行初始化 + { + mallco_dev.init(memx); + return 1; // 未初始化 + } + + // 判断这个偏移量是否超出了内存池的大小 + if (offset < memsize[memx]) // 偏移在内存池内. + { + // 内存表偏移量 = 内存池偏移量/一块内存块大小 + index = offset / memblksize[memx]; // 偏移所在内存块号码 + // 内存表数组成员的值就是申请的块数 + nmemb = mallco_dev.memmap[memx][index]; // 内存块数量 + + for (i = 0; i < nmemb; i++) // 内存块清零 + { + // 清除申请空间在内存表的标记 + mallco_dev.memmap[memx][index + i] = 0; + } + return 0; + } + else + return 1; // 偏移超区了. +} + +/***************************************************************** + *函数功能 :分配内存(外部调用) + *函数名 :mymalloc + *函数参数 :uint8_t memx,uint32_t size + *函数返回值:void * + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * size:内存大小(字节) + * 返回值:分配到的内存首地址 + ******************************************************************/ +void *mymalloc(uint8_t memx, uint32_t size) +{ + uint32_t offset; // 在内存池数组的偏移量变量 + // 获取在内存池数组的偏移量 + offset = my_mem_malloc(memx, size); + // 如果申请错误,则返回空地址 + if (offset == 0XFFFFFFFF) + return NULL; + // 如果申请成功,则返回申请空间首地址 + else + return (void *)((uint32_t)mallco_dev.membase[memx] + offset); +} + +/***************************************************************** + *函数功能 :释放内存(外部调用) + *函数名 :myfree + *函数参数 :uint8_t memx,void *ptr + *函数返回值:uint32_t + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * ptr :要释放的内存空间首地址 + ******************************************************************/ +void myfree(uint8_t memx, void *ptr) +{ + uint32_t offset; + uint32_t n; // 该要释放的空间的空间大小 + if (ptr == NULL) + return; // 地址为0. + // 确定申请空间的内存池偏移量 + offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx]; + // 空间占内存池空间的大小 + n = mallco_dev.memmap[memx][offset / memblksize[memx]] * memblksize[memx]; + // 释放内存池对应空间的数据 + mymemset(ptr, 0, n); + // 释放内存表 + my_mem_free(memx, offset); // 释放内存 +} + +/***************************************************************** + *函数功能 :重新分配内存(外部调用) + *函数名 :myfree + *函数参数 :uint8_t memx,void *ptr + *函数返回值:uint32_t + *描述 : + * memx:所属内存块,即是内部SRAM还是外部SRAM的内存块 + * ptr :旧内存空间地址首地址 + * size:要重新分配的内存大小(字节) + ******************************************************************/ +void *myrealloc(uint8_t memx, void *ptr, uint32_t size) +{ + uint32_t offset; + + // 申请一个新的空间 + offset = my_mem_malloc(memx, size); + if (offset == 0XFFFFFFFF) + return NULL; + else + { + // 把旧空间的数据复制到新空间里 + mymemcpy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size); // 拷贝旧内存内容到新内存 + // 删掉旧空间 + myfree(memx, ptr); // 释放旧内存 + // 返回新空间地址 + return (void *)((uint32_t)mallco_dev.membase[memx] + offset); // 返回新内存首地址 + } +} diff --git a/lib/src/mlist.c b/lib/src/mlist.c new file mode 100644 index 0000000..38db5e4 --- /dev/null +++ b/lib/src/mlist.c @@ -0,0 +1,149 @@ +/* + * @Author: + * @Date: 2023-04-04 08:39:23 + * @LastEditors: xxx + * @LastEditTime: 2023-04-21 12:08:31 + * @Description: + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "../inc/mlist.h" + +#ifndef NULL +#define NULL ((void *)0) +#endif + +void list_init(list_head_t *const ptr) +{ + (ptr)->next = (ptr); + (ptr)->prev = (ptr); +} + +/* + * 在两个连续的链表元素中插入一个新的元素 + */ +static void __list_add(list_head_t *const new_entry, + list_head_t *const prev, + list_head_t *const next) +{ + next->prev = new_entry; + new_entry->next = next; + new_entry->prev = prev; + prev->next = new_entry; +} + +/** + * 在指定的位置之前插入一个元素 + */ +void list_insert_forwards(list_head_t *const new_entry, list_head_t *const pos) +{ + __list_add(new_entry, pos->prev, pos); +} + +/** + * 在指定的位置之后插入一个元素 + */ +void list_insert_backwards(list_head_t *const new_entry, list_head_t *const pos) +{ + __list_add(new_entry, pos, pos->next); +} + +/** + * 在链表尾部插入新的元素 + */ +void list_add_to_tail(list_head_t *const new_entry, list_head_t *const list) +{ + __list_add(new_entry, list->prev, list); +} + +/** + * 在链表头后插入新的元素 + */ +void list_add_to_head(list_head_t *const new_entry, list_head_t *const list) +{ + __list_add(new_entry, list, list->next); +} + +static void __list_del(list_head_t *const prev, list_head_t *const next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * 删除指定的链表元素 + */ +void list_del(list_head_t *const elem) +{ + __list_del(elem->prev, elem->next); + elem->next = (list_head_t *)NULL; + elem->prev = (list_head_t *)NULL; +} + +/* + * 删除并返回链表尾元素 + */ +list_head_t *list_curtail(const list_head_t *const head) +{ + list_head_t *tail = head->prev; + list_del(tail); + return tail; +} + +/** + * 判断链表是否为空 + */ +bool list_empty(const list_head_t *const head) +{ + return (((head)->next == head) || (head->next == NULL)); +} + +/** + * 获取链表第一个元素 + */ +list_head_t *list_first_elem_look(const list_head_t *const head) +{ + if (!list_empty(head)) + { + return head->next; + } + return NULL; +} + +/** + * 从制定位置后取出并删除该元素 + */ +list_head_t *list_next_elem_get(const list_head_t *const pos) +{ + if (pos == NULL) + { + return NULL; + } + + list_head_t *temp = (pos)->next; + if (temp != NULL) + { + list_del(temp); + } + + return temp; +} + +/** + * 将链表元素从一个队列移出,再添加到另外一个队列中 + */ +void list_move_to_another_head(list_head_t *const elem, list_head_t *const head) +{ + __list_del(elem->prev, elem->next); + list_add_to_head(elem, head); +} + +/** + * 将元素从一个队列中取出,然后再放入另外一个队列的尾部; + */ +void list_move_to_another_tail(list_head_t *const elem, list_head_t *const head) +{ + __list_del(elem->prev, elem->next); + list_add_to_tail(elem, head); +} diff --git a/lib/src/pbuf.c b/lib/src/pbuf.c new file mode 100644 index 0000000..e75933e --- /dev/null +++ b/lib/src/pbuf.c @@ -0,0 +1,401 @@ +/* + * pbuf.c + * + * Created on: 2022年12月5日 + * Author: xushenghao + */ +#include "../inc/debug.h" +#include "../inc/pbuf.h" +#include "../inc/osel_arch.h" +#define PBUF_DATA_SIZE(pbuf) (pbuf->end - pbuf->head) + +typedef struct _pbuf_type_t +{ + uint8_t type; + uint16_t size; + uint8_t num; +} pbuf_type_t; + +static list_head_t pbuf_freez_blocks[PBUF_TYPE_MAX_NUM]; +uint8_t pbuf_cnt[PBUF_TYPE_MAX_NUM] = {0}; + +#if PBUF_DBG_EN > 0 +static pbuf_t *pbuf_used_p[PBUF_TYPE_MAX_NUM][PBUF_NUM_MAX]; +#endif + +static void poly_type_pbuf_init(uint8_t type, uint16_t pkt_len, uint8_t num) +{ + void *mem = NULL; + pbuf_t *pbuf = NULL; + + list_init(&pbuf_freez_blocks[type]); + + if (num == 0) + { + return; + } + + mem = osel_mem_alloc((sizeof(pbuf_t) + pkt_len) * num); + DBG_ASSERT(mem != NULL __DBG_LINE); + + for (uint8_t i = 0; i < num; i++) + { + pbuf = (pbuf_t *)((uint8_t *)mem + i * (sizeof(pbuf_t) + pkt_len)); + pbuf->head = (uint8_t *)pbuf + sizeof(pbuf_t); + pbuf->end = (uint8_t *)pbuf + sizeof(pbuf_t) + pkt_len; + pbuf->data_p = pbuf->head; + list_add_to_head(&pbuf->list, &pbuf_freez_blocks[type]); + } + + pbuf_cnt[type] = num; +} + +void pbuf_initz(void) +{ + poly_type_pbuf_init(SMALL_PBUF, + SMALL_PBUF_BUFFER_SIZE, + SMALL_PBUF_NUM); + + poly_type_pbuf_init(MEDIUM_PBUF, + MEDIUM_PBUF_BUFFER_SIZE, + MEDIUM_PBUF_NUM); + + poly_type_pbuf_init(LARGE_PBUF, + LARGE_PBUF_BUFFER_SIZE, + LARGE_PBUF_NUM); +} + +static pbuf_type_t search_free_pbuf(uint8_t pbuf_type) +{ + pbuf_type_t free_pbuf_temp; + + switch (pbuf_type) + { // 没有break让代码顺序执行 + case SMALL_PBUF: + if (!list_empty(&pbuf_freez_blocks[SMALL_PBUF])) + { + free_pbuf_temp.type = SMALL_PBUF; + free_pbuf_temp.size = SMALL_PBUF_BUFFER_SIZE; + free_pbuf_temp.num = SMALL_PBUF_NUM; + } + break; + case MEDIUM_PBUF: + if (!list_empty(&pbuf_freez_blocks[MEDIUM_PBUF])) + { + free_pbuf_temp.type = MEDIUM_PBUF; + free_pbuf_temp.size = MEDIUM_PBUF_BUFFER_SIZE; + free_pbuf_temp.num = MEDIUM_PBUF_NUM; + } + break; + case LARGE_PBUF: + if (!list_empty(&pbuf_freez_blocks[LARGE_PBUF])) + { + free_pbuf_temp.type = LARGE_PBUF; + free_pbuf_temp.size = LARGE_PBUF_BUFFER_SIZE; + free_pbuf_temp.num = LARGE_PBUF_NUM; + } + break; + default: + free_pbuf_temp.type = PBUF_TYPE_INVALID; + } + return free_pbuf_temp; +} + +static pbuf_type_t pbuf_type_select(uint16_t size) +{ + pbuf_type_t free_pbuf; + if (size <= SMALL_PBUF_BUFFER_SIZE) + { + free_pbuf = search_free_pbuf(SMALL_PBUF); + } + else if ((size > SMALL_PBUF_BUFFER_SIZE) && (size <= MEDIUM_PBUF_BUFFER_SIZE)) + { + free_pbuf = search_free_pbuf(MEDIUM_PBUF); + } + else if ((size > MEDIUM_PBUF_BUFFER_SIZE) && (size <= LARGE_PBUF_BUFFER_SIZE)) + { + free_pbuf = search_free_pbuf(LARGE_PBUF); + } + else + { + DBG_ASSERT(false __DBG_LINE); + } + + if (free_pbuf.type == PBUF_TYPE_INVALID) + { + // DBG_ASSERT(false __DBG_LINE); + } + + return free_pbuf; +} + +#if PBUF_DBG_EN > 0 +static void add_to_pbuf_used_ptr(pbuf_t *pbuf, pbuf_type_t pbuf_type) +{ + // // hal_int_state_t s; + // // HAL_ENTER_CRITICAL(s); + for (uint8_t i = 0; i < PBUF_NUM_MAX; i++) + { + if (pbuf_used_p[pbuf_type.type][i] == NULL) + { + pbuf_used_p[pbuf_type.type][i] = pbuf; + break; + } + } + // // HAL_EXIT_CRITICAL(s); +} +#endif + +pbuf_t *pbuf_allocz(uint16_t size _PLINE1_) +{ + + pbuf_type_t avilable_pbuf_type; + pbuf_t *pbuf = NULL; + // hal_int_state_t s; + // HAL_ENTER_CRITICAL(s); + avilable_pbuf_type = pbuf_type_select(size); + if (avilable_pbuf_type.type == PBUF_TYPE_INVALID) + { + return NULL; + } + pbuf = list_entry_decap(&pbuf_freez_blocks[avilable_pbuf_type.type], + pbuf_t, + list); + pbuf_cnt[avilable_pbuf_type.type]--; + // HAL_EXIT_CRITICAL(s); + + DBG_ASSERT(pbuf != NULL __DBG_LINE); + if (pbuf == NULL) + { + return NULL; + } + + osel_memset(pbuf->head, 0, avilable_pbuf_type.size); + osel_memset((uint8_t *)&pbuf->attri, 0, sizeof(pbuf->attri)); + + // HAL_ENTER_CRITICAL(s); + pbuf->used = true; + pbuf->data_len = 0; + pbuf->data_p = pbuf->head; + list_init(&pbuf->list); + // HAL_EXIT_CRITICAL(s); + +#if PBUF_DBG_EN > 0 + pbuf->alloc_line = line; + pbuf->free_line = 0; + add_to_pbuf_used_ptr(pbuf, avilable_pbuf_type); +#endif + return pbuf; +} + +static pbuf_type_t get_pbuf_type(pbuf_t **pbuf) +{ + pbuf_type_t current_pbuf_type; + uint16_t size_temp; + + DBG_ASSERT(*pbuf != NULL __DBG_LINE); + DBG_ASSERT(pbuf != NULL __DBG_LINE); + + size_temp = (*pbuf)->end - (*pbuf)->head; + + if (size_temp == SMALL_PBUF_BUFFER_SIZE) + { + current_pbuf_type.type = SMALL_PBUF; + current_pbuf_type.size = SMALL_PBUF_BUFFER_SIZE; + current_pbuf_type.num = SMALL_PBUF_NUM; + } + + else if (size_temp == MEDIUM_PBUF_BUFFER_SIZE) + { + current_pbuf_type.type = MEDIUM_PBUF; + current_pbuf_type.size = MEDIUM_PBUF_BUFFER_SIZE; + current_pbuf_type.num = MEDIUM_PBUF_NUM; + } + + else if (size_temp == LARGE_PBUF_BUFFER_SIZE) + { + current_pbuf_type.type = LARGE_PBUF; + current_pbuf_type.size = LARGE_PBUF_BUFFER_SIZE; + current_pbuf_type.num = LARGE_PBUF_NUM; + } + else + { + DBG_ASSERT(false __DBG_LINE); + } + + return current_pbuf_type; +} + +#if PBUF_DBG_EN > 0 +static void delete_from_pbuf_used_ptr(pbuf_t **pbuf, pbuf_type_t pbuf_type_temp) +{ + // hal_int_state_t s; + // HAL_ENTER_CRITICAL(s); + + for (uint8_t i = 0; i < PBUF_NUM_MAX; i++) + { + if (pbuf_used_p[pbuf_type_temp.type][i] == *pbuf) + { + pbuf_used_p[pbuf_type_temp.type][i] = NULL; + break; + } + } + + // HAL_EXIT_CRITICAL(s); +} +#endif + +void pbuf_freez(pbuf_t **const pbuf _PLINE2_) +{ + + pbuf_type_t pbuf_type; + // hal_int_state_t s; + DBG_ASSERT(*pbuf != NULL __DBG_LINE); + DBG_ASSERT(pbuf != NULL __DBG_LINE); + DBG_ASSERT((*pbuf)->used == true __DBG_LINE); // 用于检测嵌套的重复释放 + + if (pbuf == NULL || *pbuf == NULL || (*pbuf)->used == false) + { + return; + } + + pbuf_type = get_pbuf_type(pbuf); + +#if PBUF_DBG_EN > 0 + delete_from_pbuf_used_ptr(pbuf, pbuf_type); + (*pbuf)->free_line = line; +#endif + osel_memset((*pbuf)->head, 0, pbuf_type.size); + osel_memset((uint8_t *)&((*pbuf)->attri), 0, sizeof((*pbuf)->attri)); + + // HAL_ENTER_CRITICAL(s); + + if ((*pbuf)->data_len > ((*pbuf)->end - (*pbuf)->head)) + { + DBG_ASSERT(false __DBG_LINE); + } + + (*pbuf)->used = false; + (*pbuf)->data_len = 0; + (*pbuf)->data_p = (*pbuf)->head; + list_init(&(*pbuf)->list); + + list_add_to_tail(&(*pbuf)->list, &pbuf_freez_blocks[pbuf_type.type]); + +#if PBUF_DBG_EN > 0 + uint8_t list_cnt = 0; + list_count(&pbuf_freez_blocks[pbuf_type.type], list_cnt); + DBG_ASSERT(list_cnt != 0 __DBG_LINE); +#endif + + pbuf_cnt[pbuf_type.type]++; + +#if PBUF_DBG_EN > 0 + (*pbuf)->alloc_line = 0; +#endif + + // HAL_EXIT_CRITICAL(s); + + *pbuf = NULL; +} + +uint8_t *pbuf_skip_datap_forward(pbuf_t *const pbuf, uint8_t len) +{ + uint8_t *datap_tmp = NULL; + // hal_int_state_t s; + DBG_ASSERT(pbuf != NULL __DBG_LINE); + if (pbuf == NULL) + { + return NULL; + } + + // HAL_ENTER_CRITICAL(s); + + if ((pbuf->data_p + len) > pbuf->end) + { + // HAL_EXIT_CRITICAL(s); + return NULL; + } + + pbuf->data_p += len; + datap_tmp = pbuf->data_p; + + // HAL_EXIT_CRITICAL(s); + return datap_tmp; +} + +uint8_t *pbuf_skip_datap_backward(pbuf_t *const pbuf, uint8_t len) +{ + uint8_t *datap_tmp = NULL; + // hal_int_state_t s; + DBG_ASSERT(pbuf != NULL __DBG_LINE); + if (pbuf == NULL) + { + return NULL; + } + + // HAL_ENTER_CRITICAL(s); + + if ((pbuf->data_p - len) < pbuf->head) + { + // HAL_EXIT_CRITICAL(s); + return NULL; + } + + pbuf->data_p -= len; + datap_tmp = pbuf->data_p; + + // HAL_EXIT_CRITICAL(s); + return datap_tmp; +} + +bool pbuf_copy_data_in(pbuf_t *const pbuf, const uint8_t *const src, uint8_t len) +{ + DBG_ASSERT(pbuf != NULL __DBG_LINE); + if (pbuf == NULL) + { + return false; + } + // hal_int_state_t s; + // HAL_ENTER_CRITICAL(s); + + if ((pbuf->data_p + len) > pbuf->end) + { + // HAL_EXIT_CRITICAL(s); + return false; + } + else + { + osel_memcpy(pbuf->data_p, src, len); + pbuf->data_p += len; + pbuf->data_len += len; + + // HAL_EXIT_CRITICAL(s); + return true; + } +} + +bool pbuf_copy_data_out(uint8_t *const dst, pbuf_t *const pbuf, uint8_t len) +{ + DBG_ASSERT(pbuf != NULL __DBG_LINE); + if (pbuf == NULL) + { + return false; + } + // hal_int_state_t s; + // HAL_ENTER_CRITICAL(s); + + if ((pbuf->data_p + len) > pbuf->end) + { + // HAL_EXIT_CRITICAL(s); + return false; + } + else + { + osel_memcpy(dst, pbuf->data_p, len); + pbuf->data_p += len; + + // HAL_EXIT_CRITICAL(s); + return true; + } +} diff --git a/lib/src/sqqueue.c b/lib/src/sqqueue.c new file mode 100644 index 0000000..dc5d1a8 --- /dev/null +++ b/lib/src/sqqueue.c @@ -0,0 +1,450 @@ +/** + * @file sqqueue.c + * @author xxx + * @date 2023-06-25 13:07:02 + * @brief 提供循环队列功能 + * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "../inc/sqqueue.h" +#include "../inc/osel_arch.h" + +#define SQQ_ENTRY_SIZE (queue_ptr->entry_size) +#define SQQ_LEN (queue_ptr->sqq_len) + +/** + * @brief 初始化队列控制结构体 + * @param p_this 队列控制结构体的指针 + * @param entry_size 队列中每个元素的大小 + * @param sqq_len 队列的最大长度 + * @return {BOOL} 初始化成功返回true,否则返回false + * @note 此函数用于初始化队列控制结构体,确保队列有足够的空间存储元素 + */ +static BOOL sqqueue_init(sqqueue_ctrl_t *const p_this, + uint8_t entry_size, + uint16_t sqq_len) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + sqqueue_t *queue_ptr = &(p_this->sqq); + + if (queue_ptr != NULL) + { + queue_ptr->entry_size = entry_size; + queue_ptr->sqq_len = sqq_len + 1; + if (queue_ptr->base != NULL) + { + osel_mem_free(queue_ptr->base); + queue_ptr->base = NULL; + } + + queue_ptr->base = (uint8_t *)osel_mem_alloc(SQQ_LEN * SQQ_ENTRY_SIZE); + if (queue_ptr->base == NULL) + { + return FALSE; + } + queue_ptr->front = 0; + queue_ptr->rear = 0; + + return TRUE; + } + + return FALSE; +} + +/** + * @brief 销毁顺序队列 + * + * 销毁给定的顺序队列,释放其占用的内存空间。 + * + * @param p_this 顺序队列控制块指针 + */ +void sqqueue_dinit(sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + sqqueue_t *queue_ptr = &(p_this->sqq); + + if (queue_ptr != NULL) + { + if (queue_ptr->base != NULL) + { + osel_mem_free(queue_ptr->base); + queue_ptr->base = NULL; + } + } +} + +/** + * @brief 获取队列的长度 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {uint16_t} 队列的长度 + * @note 此函数用于获取队列的长度,包括队列中元素的数量和最大长度 + */ +static uint16_t sqqueue_length(const sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + const sqqueue_t *const queue_ptr = &(p_this->sqq); + uint16_t length = 0; + if (p_this != NULL) + { + length = (queue_ptr->rear + SQQ_LEN - queue_ptr->front); + + if (length >= SQQ_LEN) + { + length -= SQQ_LEN; + } + } + + return length; +} + +/** + * @brief 获取队列的长度 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {uint16_t} 队列的长度 + * @note 此函数用于获取队列的长度,包括队列中元素的数量和最大长度 + */ +static BOOL sqqueue_full(const sqqueue_ctrl_t *const p_this) +{ + uint16_t rear = 0; + + DBG_ASSERT(p_this != NULL __DBG_LINE); + + if (p_this != NULL) + { + const sqqueue_t *const queue_ptr = &(p_this->sqq); + rear = queue_ptr->rear + 1; + if (rear >= SQQ_LEN) + { + rear -= SQQ_LEN; + } + if (rear == queue_ptr->front) + { + return TRUE; + } + } + + return FALSE; +} + +/** + * @brief 将元素添加到队列中 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @param {void} *e 要添加的元素 + * @return {BOOL} 添加成功返回true,否则返回false + * @note 此函数用于将元素添加到队列中,确保队列有足够的空间存储元素 + */ +static BOOL enter_sqqueue(sqqueue_ctrl_t *const p_this, const void *const e) +{ + uint16_t rear = 0; + sqqueue_t *queue_ptr = &(p_this->sqq); + + if ((p_this != NULL) && (e != NULL)) + { + rear = queue_ptr->rear + 1; + if (rear >= SQQ_LEN) + { + rear -= SQQ_LEN; + } + + if (rear == queue_ptr->front) + { + return FALSE; + } + + /* 根据e的长度进行内存拷贝 */ + DBG_ASSERT(queue_ptr->rear != SQQ_LEN __DBG_LINE); + osel_memcpy(queue_ptr->base + (queue_ptr->rear * SQQ_ENTRY_SIZE), + e, + SQQ_ENTRY_SIZE); + queue_ptr->rear = rear; + + return TRUE; + } + return FALSE; +} + +/** + * @brief 将字符串添加到队列中 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @param {const void} *string 要添加的字符串 + * @param {uint16_t} cnt 要添加的字符串的长度 + * @return {BOOL} 添加成功返回true,否则返回false + * @note 此函数用于将字符串添加到队列中,确保队列有足够的空间存储字符串 + */ +static BOOL string_enter_sqqueue(sqqueue_ctrl_t *const p_this, + const void *const string, + uint16_t cnt) +{ + uint16_t rear = 0; + uint16_t length = 0; + sqqueue_t *queue_ptr = &(p_this->sqq); + + if ((p_this != NULL) && (string != NULL)) + { + /* 判断是否超出队列长度 */ + length = sqqueue_length(p_this); // 已有元素个数 + if (length == 0xFFFF) + { + return FALSE; + } + + length = (SQQ_LEN - 1) - length; // 可写入个数 + if (length < cnt) + { + return FALSE; + } + + rear = queue_ptr->rear + cnt; + if (rear >= SQQ_LEN) + { + rear -= SQQ_LEN; + uint8_t half = SQQ_LEN - queue_ptr->rear; + osel_memcpy(queue_ptr->base + (queue_ptr->rear * SQQ_ENTRY_SIZE), + string, half * SQQ_ENTRY_SIZE); + uint8_t *half_p = (uint8_t *)string; + osel_memcpy(queue_ptr->base, (uint8_t *)&half_p[half], rear * SQQ_ENTRY_SIZE); + } + else + { + osel_memcpy(queue_ptr->base + (queue_ptr->rear * SQQ_ENTRY_SIZE), + string, SQQ_ENTRY_SIZE * cnt); + } + + queue_ptr->rear = rear; + + return TRUE; + } + return FALSE; +} + +/** + * @brief 从队列中删除元素 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {void *} 删除的元素,如果队列为空则返回NULL + * @note 此函数用于从队列中删除元素,确保队列中有足够的空间存储元素 + */ +static void *delete_sqqueue(sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + uint16_t front = 0; + + sqqueue_t *queue_ptr = NULL; + + if (p_this != NULL) + { + void *p_elem = NULL; + queue_ptr = &(p_this->sqq); + if (queue_ptr->rear == queue_ptr->front) + { + return NULL; + } + /* 根据元素类型大小计算出偏移量,得到该元素首地址 */ + p_elem = (void *)((queue_ptr->base) + (queue_ptr->front * SQQ_ENTRY_SIZE)); + front = queue_ptr->front + 1; + if (front >= SQQ_LEN) + { + front -= SQQ_LEN; + } + queue_ptr->front = front; + + return p_elem; + } + return NULL; +} + +/** + * @brief 撤销队列中的一个元素 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {*} 返回被撤销的元素,如果队列为空则返回NULL + * @note + */ +static void *revoke_sqqueue(sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + uint16_t rear = 0; + sqqueue_t *queue_ptr = NULL; + + if (p_this != NULL) + { + void *p_elem = NULL; + + queue_ptr = &(p_this->sqq); + if (queue_ptr->rear == queue_ptr->front) + { + return NULL; + } + + rear = queue_ptr->rear; + if (rear == 0) + { + rear = SQQ_LEN - 1; + } + else + { + rear--; + } + queue_ptr->rear = rear; + /* 根据元素类型大小计算出偏移量,得到该元素首地址*/ + p_elem = (void *)((queue_ptr->base) + (queue_ptr->rear * SQQ_ENTRY_SIZE)); + + return p_elem; + } + return NULL; +} + +/** + * @brief 清空队列 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {void} + * @note + */ +static void clear_sqq(sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + + sqqueue_t *queue_ptr = &(p_this->sqq); + if (p_this != NULL) + { + queue_ptr->front = 0; + queue_ptr->rear = 0; + } +} + +/** + * @brief 遍历队列 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @param {void (*)(const void *e)} vi 遍历函数,参数为队列中的一个元素 + * @return {void} + * @note + */ +static void traverse(sqqueue_ctrl_t *const p_this, void (*vi)(const void *e)) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + sqqueue_t *queue_ptr = NULL; + uint16_t i = 0; + + if (p_this != NULL) + { + queue_ptr = &(p_this->sqq); + + if (queue_ptr->rear == queue_ptr->front) + { + return; + } + + i = queue_ptr->front; + while (i != queue_ptr->rear) + { + vi((void *)((queue_ptr->base) + (i * SQQ_ENTRY_SIZE))); + if (++i >= SQQ_LEN) + { + i = 0; + } + } + } +} + +/** + * @brief 从队列中删除一个元素 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @param {uint16_t} offset_to_front 要删除的元素在队列中的偏移量,从队头开始 + * @return {void} + * @note + */ +static void qremove(sqqueue_ctrl_t *const p_this, uint16_t offset_to_front) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + sqqueue_t *queue_ptr = NULL; + uint16_t i = 0; + + if (p_this != NULL) + { + queue_ptr = &(p_this->sqq); + DBG_ASSERT(offset_to_front < SQQ_LEN __DBG_LINE); + + if (queue_ptr->rear == queue_ptr->front) + { + return; + } + + uint16_t j = 0; + + for (i = offset_to_front; i > 0; i--) + { + /* 定位待删除元素在队列中的位置 */ + j = queue_ptr->front + i; + + if (j >= SQQ_LEN) + { + j -= SQQ_LEN; + } + + if (j == 0) // 在翻转位置特殊处理拷贝的源地址 + { + osel_memcpy(queue_ptr->base + (0 * SQQ_ENTRY_SIZE), + queue_ptr->base + ((SQQ_LEN - 1) * SQQ_ENTRY_SIZE), + SQQ_ENTRY_SIZE); + } + else + { + osel_memcpy(queue_ptr->base + (j * SQQ_ENTRY_SIZE), + queue_ptr->base + ((j - 1) * SQQ_ENTRY_SIZE), + SQQ_ENTRY_SIZE); + } + } + + /* 减少队列长度 */ + uint16_t front = queue_ptr->front + 1; + if (front >= SQQ_LEN) + { + front -= SQQ_LEN; + } + + queue_ptr->front = front; + } +} + +/** + * @brief 初始化队列控制结构体 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @param {uint8_t} entry_size 队列中每个元素的类型大小 + * @param {uint16_t} sqq_len 队列的最大长度 + * @return {BOOL} 初始化成功返回true,否则返回false + * @note + */ +BOOL sqqueue_ctrl_init(sqqueue_ctrl_t *const p_this, + uint8_t entry_size, + uint16_t sqq_len) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + + if (p_this != NULL) + { + if (sqqueue_init(p_this, entry_size, sqq_len) != FALSE) + { + p_this->enter = enter_sqqueue; + p_this->string_enter = string_enter_sqqueue; + p_this->del = delete_sqqueue; + p_this->revoke = revoke_sqqueue; + p_this->get_len = sqqueue_length; + p_this->full = sqqueue_full; + p_this->clear_sqq = clear_sqq; + p_this->traverse = traverse; + p_this->remove = qremove; + return TRUE; + } + } + return FALSE; +} + +/** + * @brief 销毁队列控制结构体 + * @param {sqqueue_ctrl_t} *p_this 队列控制结构体的指针 + * @return {void} + * @note + */ +void sqqueue_ctrl_dinit(sqqueue_ctrl_t *const p_this) +{ + DBG_ASSERT(p_this != NULL __DBG_LINE); + sqqueue_dinit(p_this); +} diff --git a/lib/src/storage.c b/lib/src/storage.c new file mode 100644 index 0000000..cf67317 --- /dev/null +++ b/lib/src/storage.c @@ -0,0 +1,252 @@ +#include "storage.h" + +/** + * @brief 根据索引查找存储节点 + * + * 在给定的存储结构中,根据索引查找对应的存储节点。 + * + * @param storage 存储结构指针 + * @param index 要查找的索引值 + * + * @return 指向找到的存储节点的指针,如果未找到则返回 NULL + */ +static storage_node_t *storage_node_find(storage_t *storage, uint16_t index) +{ + storage_node_t *node = NULL; + for (clist_node_t *p = storage->head; p != NULL; p = p->Next) + { + node = (storage_node_t *)p->data; + if (node->index == index) + { + break; + } + else + { + node = NULL; + } + } + + return node; +} +/** + * @brief 初始化存储结构 + * + * 根据给定的基地址和页大小,初始化存储结构。 + * + * @param base_addr 存储的基地址 + * @param page_size 存储的页大小 + * + * @return 指向存储结构的指针 + */ +storage_t *storage_init(uint32_t base_addr, uint16_t page_size) +{ + storage_t *storage = (storage_t *)osel_mem_alloc(sizeof(storage_t)); + DBG_ASSERT(storage != NULL __DBG_LINE); + osel_memset((uint8_t *)storage, 0, sizeof(storage_t)); + clist_init(&storage->head); + + storage->params.base_addr = base_addr; + storage->params.page_size = page_size; + return storage; +} + +/** + * @brief 销毁存储结构 + * + * 销毁给定的存储结构,并释放其占用的内存。 + * + * @param storage 指向存储结构的指针 + */ +void storage_destroy(storage_t *storage) +{ + if (storage != NULL) + { + clist_destroy(&storage->head); + osel_mem_free(storage); + } +} + +/** + * @brief 向存储结构中添加一个新的节点 + * + * 在给定的存储结构中添加一个新的节点。节点的索引和大小由参数指定。 + * + * @param storage 指向存储结构的指针 + * @param index 新节点的索引 + * @param size 新节点的大小 + */ +void storage_add_node(storage_t *storage, uint16_t index, uint16_t size) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + storage_node_t *node = (storage_node_t *)osel_mem_alloc(sizeof(storage_node_t)); + node->index = index; + node->size = size; + node->address = storage->params.base_addr + storage->params.variable_size; + storage->params.variable_size += size; + storage->params.variable_count++; + storage->params.page_count = storage->params.variable_size / storage->params.page_size; + if (storage->params.variable_size % storage->params.page_size != 0) + { + storage->params.page_count++; + } + clist_push_back(&storage->head, (cnode)node); +} + +/** + * @brief 将缓冲区中的所有数据写入存储设备 + * + * 将指定缓冲区中的数据写入存储设备。 + * + * @param storage 存储设备的指针 + * @param buf 待写入数据的缓冲区指针 + * + * @return 如果写入成功,则返回TRUE;否则返回FALSE + */ +BOOL storage_write_all(storage_t *storage, const uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.write != NULL __DBG_LINE); + return storage->ops.write(storage->params.base_addr, (uint8_t *)buf, storage->params.variable_size); +} + +/** + * @brief 向存储设备写入数据 + * + * 将指定的数据写入到存储设备中指定的索引位置。 + * + * @param storage 存储设备指针 + * @param index 索引位置 + * @param buf 要写入的数据缓冲区指针 + * + * @return 写入成功返回TRUE,失败返回FALSE + */ +BOOL storage_write(storage_t *storage, uint16_t index, const uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.write != NULL __DBG_LINE); + storage_node_t *node = storage_node_find(storage, index); + if (node == NULL) + { + return FALSE; + } + + return storage->ops.write(node->address, (uint8_t *)buf, node->size); +} + +/** + * @brief 从存储设备中读取所有数据到缓冲区 + * + * 从指定的存储设备中读取数据,并将数据存储在提供的缓冲区中。 + * + * @param storage 存储设备的指针 + * @param buf 用于存储读取数据的缓冲区指针 + * + * @return 如果读取成功,则返回 TRUE;否则返回 FALSE + * + * @note 必须在调用此函数之前,确保提供的存储设备和缓冲区指针均不为空,且要读取的数据大小不为零。 + * 此外,存储设备的读取操作函数也必须不为空。 + */ +BOOL storage_read_all(storage_t *storage, uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.read != NULL __DBG_LINE); + return storage->ops.read(storage->params.base_addr, buf, storage->params.variable_size); +} + +/** + * @brief 从存储中读取数据 + * + * 从指定的存储位置读取数据到缓冲区中。 + * + * @param storage 存储指针,指向存储结构的指针 + * @param index 要读取的数据的索引 + * @param buf 指向存储读取数据的缓冲区的指针 + * + * @return 如果读取成功,返回TRUE;否则返回FALSE + */ +BOOL storage_read(storage_t *storage, uint16_t index, uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.read != NULL __DBG_LINE); + storage_node_t *node = storage_node_find(storage, index); + if (node == NULL) + { + return FALSE; + } + + return storage->ops.read(node->address, buf, node->size); +} + +/** + * @brief 检查存储中的数据是否与给定的缓冲区内容相同 + * + * 检查给定索引位置的存储节点中的数据是否与给定的缓冲区内容相同。 + * + * @param storage 存储对象指针 + * @param index 要检查的存储节点的索引 + * @param buf 要比较的缓冲区指针 + * + * @return 如果存储节点中的数据与缓冲区内容相同,则返回 TRUE;否则返回 FALSE + */ +BOOL storage_check(storage_t *storage, uint16_t index, uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.read != NULL __DBG_LINE); + BOOL ret = FALSE; + storage_node_t *node = storage_node_find(storage, index); + if (node == NULL) + { + return FALSE; + } + uint8_t *tmp = (uint8_t *)osel_mem_alloc(node->size); + DBG_ASSERT(tmp != NULL __DBG_LINE); + if (storage->ops.read(node->address, tmp, node->size) == TRUE) + { + if (osel_memcmp(tmp, buf, node->size) == 0) + { + ret = TRUE; + } + } + osel_mem_free(tmp); + return ret; +} + +/** + * @brief 检查存储区内容是否与给定的缓冲区内容一致 + * + * 该函数会检查存储区(由storage参数指定)的内容是否与给定的缓冲区(由buf参数指定)内容一致。 + * + * @param storage 存储区指针,指向一个存储区结构体 + * @param buf 缓冲区指针,指向要比较的缓冲区 + * + * @return 如果存储区内容与缓冲区内容一致,则返回TRUE;否则返回FALSE + * + * @note 调用此函数前,需要确保storage和buf均不为NULL,且storage->ops.read指向有效的读操作函数。 + */ +BOOL storage_check_all(storage_t *storage, uint8_t *buf) +{ + DBG_ASSERT(storage != NULL __DBG_LINE); + DBG_ASSERT(buf != NULL __DBG_LINE); + DBG_ASSERT(storage->ops.read != NULL __DBG_LINE); + BOOL ret = FALSE; + uint8_t *tmp = (uint8_t *)osel_mem_alloc(storage->params.variable_size); + DBG_ASSERT(tmp != NULL __DBG_LINE); + if (storage->ops.read(storage->params.base_addr, tmp, storage->params.variable_size) == TRUE) + { + if (osel_memcmp(tmp, buf, storage->params.variable_size) == 0) + { + ret = TRUE; + } + else + { + __NOP(); + } + } + osel_mem_free(tmp); + return ret; +} diff --git a/lib/src/wl_flash.c b/lib/src/wl_flash.c new file mode 100644 index 0000000..10cd9eb --- /dev/null +++ b/lib/src/wl_flash.c @@ -0,0 +1,314 @@ +#include "wl_flash.h" +#include +static BOOL flash_is_written(wl_flash_t *cfg, uint32_t address, uint16_t length); + +/** + * @brief 初始化闪存 + * + * 根据给定的闪存配置信息,初始化闪存的相关参数和状态。 + * + * @param cfg 闪存配置结构体指针 + * + * @note 该函数会检查cfg是否为空,并断言cfg中的srand、read、write、erase_page操作均不为空。 + * @note 同时还会检查FLASH页大小是否为2的幂次方,FLASH长度是否为页大小的整数倍,以及FLASH长度是否大于等于data_size。 + * @note 如果data_size不是write_gran的整数倍,则会向上取整。 + * @note 初始化完成后,将更新起始地址、起始页号、总页数、数据块所占页数和剩余大小等参数。 + */ +void wl_flash_init(wl_flash_t *cfg) +{ + // 断言cfg不为空 + DBG_ASSERT(cfg != NULL __DBG_LINE); + + // 断言cfg中的srand操作不为空 + DBG_ASSERT(cfg->ops.srand != NULL __DBG_LINE); + // 断言cfg中的read操作不为空 + DBG_ASSERT(cfg->ops.read != NULL __DBG_LINE); + // 断言cfg中的write操作不为空 + DBG_ASSERT(cfg->ops.write != NULL __DBG_LINE); + // 断言cfg中的erase_page操作不为空 + DBG_ASSERT(cfg->ops.erase_page != NULL __DBG_LINE); + + // 检查FLASH页大小是否为2的幂次方 + // 检查FLASH页大小 + DBG_ASSERT((cfg->page_size & (cfg->page_size - 1)) == 0 __DBG_LINE); + + // 检查FLASH长度是否为页大小的整数倍 + // 检查FLASH长度 + DBG_ASSERT(cfg->len % cfg->page_size == 0 __DBG_LINE); + + // 如果data_size不是write_gran的整数倍,则向上取整 + if (cfg->data_size % cfg->write_gran != 0) + { + cfg->data_size = (cfg->data_size / cfg->write_gran + 1) * cfg->write_gran; + } + + // 断言FLASH长度大于等于data_size + DBG_ASSERT(cfg->len >= cfg->data_size __DBG_LINE); + + // 初始化起始地址、起始页号、总页数、数据块所占页数和剩余大小 + cfg->private.current_address = cfg->addr; + cfg->private.start_page = cfg->addr / cfg->page_size; + cfg->private.page_total = cfg->len / cfg->page_size; + if (cfg->wl_flag == TRUE) + { + cfg->private.block_page = cfg->data_size / cfg->page_size + (cfg->data_size % cfg->page_size != 0 ? 1 : 0); + } + else + { + cfg->private.block_page = cfg->private.page_total; + } + + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; +} + +/** + * @brief 擦除指定范围内的闪存 + * + * 根据给定的闪存配置信息,擦除指定范围内的闪存页面。 + * + * @param cfg 闪存配置结构体指针 + * + * @note 调用此函数前,请确保 cfg 不为 NULL,并且 cfg 中的 srand、erase_page 函数指针不为 NULL, + * write_gran 不为 0。 + * + * @see DBG_ASSERT 宏定义用于断言条件 + */ +void wl_flash_erase(wl_flash_t *cfg) +{ + // 断言cfg不为空 + DBG_ASSERT(cfg != NULL __DBG_LINE); + // 断言cfg->ops.srand不为空 + DBG_ASSERT(cfg->ops.srand != NULL __DBG_LINE); + // 断言cfg->ops.erase_page不为空 + DBG_ASSERT(cfg->ops.erase_page != NULL __DBG_LINE); + // 断言cfg->write_gran不为0 + DBG_ASSERT(cfg->write_gran != 0 __DBG_LINE); + // 断言cfg->private.page_total不为0 + DBG_ASSERT(cfg->private.page_total != 0 __DBG_LINE); + + // 计算起始页号 + uint16_t start_page = cfg->addr / cfg->page_size; + // 计算结束页号 + uint16_t end_page = (cfg->addr + cfg->len) / cfg->page_size; + + // 遍历起始页号到结束页号的每个页 + for (uint16_t i = start_page; i < end_page; i++) + { + // 如果该页已被写入 + if (flash_is_written(cfg, i * cfg->page_size, cfg->page_size) == TRUE) + { + // 擦除该页 + cfg->ops.erase_page(i); + } + } + + // 如果wl_flag为TRUE + if (cfg->wl_flag == TRUE) + { + // 调用srand函数 + cfg->ops.srand(); + // 计算最大值,这里减去一是为了防止超出范围 + uint16_t max = cfg->private.page_total / cfg->private.block_page - 1; // 这里减去一是为了防止超出范围 + // 计算当前地址 + cfg->private.current_address = cfg->addr + (rand() % max) * cfg->private.block_page * cfg->page_size; + // 设置剩余大小为block_page个页的大小 + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; + } + else + { + // 否则,将当前地址设置为cfg->addr + cfg->private.current_address = cfg->addr; + // 设置剩余大小为block_page个页的大小 + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; + } +} + +/** + * @brief 获取当前闪存地址 + * + * 从给定的闪存配置结构体中获取当前闪存的地址。 + * + * @param cfg 闪存配置结构体指针 + * + * @return 当前闪存的地址值 + */ +uint32_t wl_flash_get_current_address(wl_flash_t *cfg) +{ + return cfg->private.current_address; +} + +/** + * @brief 设置下一个闪存地址 + * + * 根据给定的闪存配置信息,设置下一个要写入数据的闪存地址。 + * + * @param cfg 闪存配置指针 + * + * @note 传入参数不能为空 + */ +void wl_flash_set_next_address(wl_flash_t *cfg) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + // 断言cfg->private.page_total不为0 + DBG_ASSERT(cfg->private.page_total != 0 __DBG_LINE); + BOOL switch_page = FALSE; + uint16_t current_page = cfg->private.current_address / cfg->page_size; + if (cfg->wl_flag == TRUE) + { + // 判断是否需要换到下一个block + if ((cfg->private.residue_size - cfg->data_size) < cfg->data_size) + { + current_page += cfg->private.block_page; + cfg->private.current_address = current_page * cfg->page_size; + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; + switch_page = TRUE; + } + else + { + // 有空间可以写入,当前写入地址偏移 + cfg->private.current_address += cfg->data_size; + cfg->private.residue_size -= cfg->data_size; + } + + if (cfg->private.current_address + cfg->data_size > cfg->addr + cfg->len) + { + cfg->private.current_address = cfg->addr; + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; + switch_page = TRUE; + } + } + else + { + // 不使用平衡擦写默认都使用第一页 + cfg->private.current_address = cfg->addr; + cfg->private.residue_size = cfg->private.block_page * cfg->page_size; + switch_page = TRUE; + } + + // 如果写入区域切换到下一个block,则判断是否需要擦除 + if (switch_page == TRUE) + { + // 判断准备写入的区域是否已经写入过数据 + for (uint8_t i = 0; i < cfg->private.block_page; i++) + { + uint32_t current_address = cfg->private.current_address + (i * cfg->page_size); + if (flash_is_written(cfg, current_address, cfg->page_size) == TRUE) + { + cfg->ops.erase_page(current_address / cfg->page_size); + } + } + } +} + +/** + * @brief 设置当前 Flash 地址 + * + * 将给定的地址设置为 Flash 设备的当前地址。 + * + * @param cfg Flash 配置结构体指针 + * @param address 要设置的 Flash 地址 + */ +void wl_flash_set_current_address(wl_flash_t *cfg, uint32_t address) +{ + uint16_t current_page = address / cfg->page_size; + uint16_t current_block_page_num = (current_page - cfg->private.start_page) / cfg->private.block_page; // 确定当前的地址在第几个block_page中 + uint32_t current_page_start_address = cfg->addr + current_block_page_num * cfg->page_size * cfg->private.block_page; // 当前block_page的起始地址 + cfg->private.current_address = address; + // 根据当前地址更新剩余可写字节 + cfg->private.residue_size = cfg->private.block_page * cfg->page_size - (address - current_page_start_address); +} + +/** + * @brief 写入数据到闪存 + * + * 根据给定的配置,将数据写入到闪存中。 + * + * @param cfg 闪存配置结构体指针 + * @param buf 要写入的数据缓冲区指针 + * @param size 要写入的数据大小(字节) + * + * @return 如果写入成功,则返回 TRUE;否则返回 FALSE + */ +BOOL wl_flash_write(wl_flash_t *cfg, const uint8_t *buf, uint16_t size) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + DBG_ASSERT(cfg->ops.write != NULL __DBG_LINE); + DBG_ASSERT(cfg->write_gran != 0 __DBG_LINE); + DBG_ASSERT(size <= cfg->data_size __DBG_LINE); + // 断言cfg->private.page_total不为0 + DBG_ASSERT(cfg->private.page_total != 0 __DBG_LINE); + BOOL res = FALSE; + + res = cfg->ops.write(cfg->private.current_address, buf, size); + return res; +} + +/** + * @brief 从指定 Flash 设备中读取数据 + * + * 根据给定的 Flash 设备配置参数,从 Flash 设备的当前地址开始,读取指定长度的数据到缓冲区中。 + * + * @param cfg Flash 设备配置指针 + * @param buf 存储读取数据的缓冲区指针 + * @param size 要读取的数据大小(以字节为单位) + * + * @return 读取操作是否成功,成功返回 TRUE,否则返回 FALSE + * + * @note 调用此函数前,需要确保 Flash 设备配置正确,并且 read 操作函数已设置 + * + * @warning 请注意检查函数返回结果,以确保数据读取操作成功 + */ +BOOL wl_flash_read(wl_flash_t *cfg, uint8_t *buf, uint16_t size) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + // 断言cfg->private.page_total不为0 + DBG_ASSERT(cfg->private.page_total != 0 __DBG_LINE); + DBG_ASSERT(cfg->ops.read != NULL __DBG_LINE); + return cfg->ops.read(cfg->private.current_address, buf, size); +} + +static BOOL flash_is_written(wl_flash_t *cfg, uint32_t address, uint16_t length) +{ + DBG_ASSERT(cfg != NULL __DBG_LINE); + DBG_ASSERT(cfg->ops.read != NULL __DBG_LINE); + uint8_t *data; + uint16_t count = 0; + BOOL res = FALSE, ret = FALSE; + count = length / cfg->page_size; + if (length % cfg->page_size != 0) + { + count++; + } + data = osel_mem_alloc(cfg->page_size); + DBG_ASSERT(data != NULL __DBG_LINE); + + for (uint16_t i = 0; i < count; i++) + { + ret = cfg->ops.read(address + i * cfg->page_size, data, cfg->page_size); + if (ret == TRUE) + { + for (uint16_t j = 0; j < cfg->page_size; j++) + { + if (data[j] != 0xff) + { + res = TRUE; + break; + } + } + } + else + { + res = FALSE; + break; + } + + if (res == TRUE) + { + break; + } + res = FALSE; + ret = FALSE; + } + osel_mem_free(data); + return res; +} diff --git a/lib/unity/unity.c b/lib/unity/unity.c new file mode 100644 index 0000000..be6ae2e --- /dev/null +++ b/lib/unity/unity.c @@ -0,0 +1,2582 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#include "unity.h" + +#ifndef UNITY_PROGMEM +#define UNITY_PROGMEM +#endif + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL \ + do \ + { \ + Unity.CurrentTestFailed = 1; \ + UNITY_OUTPUT_FLUSH(); \ + TEST_ABORT(); \ + }while (0) +#define UNITY_IGNORE_AND_BAIL \ + do \ + { \ + Unity.CurrentTestIgnored = 1; \ + UNITY_OUTPUT_FLUSH(); \ + TEST_ABORT(); \ + } while (0) +#define RETURN_IF_FAIL_OR_IGNORE \ + do \ + { \ + if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) \ + { \ + TEST_ABORT(); \ + } \ + } while (0) + + struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +const char UNITY_PROGMEM UnityStrOk[] = "\033[42mOK\033[0m"; +const char UNITY_PROGMEM UnityStrPass[] = "\033[42mPASS\033[0m"; +const char UNITY_PROGMEM UnityStrFail[] = "\033[41mFAIL\033[0m"; +const char UNITY_PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[0m"; +#else +const char UNITY_PROGMEM UnityStrOk[] = "OK"; +const char UNITY_PROGMEM UnityStrPass[] = "PASS"; +const char UNITY_PROGMEM UnityStrFail[] = "FAIL"; +const char UNITY_PROGMEM UnityStrIgnore[] = "IGNORE"; +#endif +static const char UNITY_PROGMEM UnityStrNull[] = "NULL"; +static const char UNITY_PROGMEM UnityStrSpacer[] = ". "; +static const char UNITY_PROGMEM UnityStrExpected[] = " Expected "; +static const char UNITY_PROGMEM UnityStrWas[] = " Was "; +static const char UNITY_PROGMEM UnityStrGt[] = " to be greater than "; +static const char UNITY_PROGMEM UnityStrLt[] = " to be less than "; +static const char UNITY_PROGMEM UnityStrOrEqual[] = "or equal to "; +static const char UNITY_PROGMEM UnityStrNotEqual[] = " to be not equal to "; +static const char UNITY_PROGMEM UnityStrElement[] = " Element "; +static const char UNITY_PROGMEM UnityStrByte[] = " Byte "; +static const char UNITY_PROGMEM UnityStrMemory[] = " Memory Mismatch."; +static const char UNITY_PROGMEM UnityStrDelta[] = " Values Not Within Delta "; +static const char UNITY_PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char UNITY_PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char UNITY_PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char UNITY_PROGMEM UnityStrNot[] = "Not "; +static const char UNITY_PROGMEM UnityStrInf[] = "Infinity"; +static const char UNITY_PROGMEM UnityStrNegInf[] = "Negative Infinity"; +static const char UNITY_PROGMEM UnityStrNaN[] = "NaN"; +static const char UNITY_PROGMEM UnityStrDet[] = "Determinate"; +static const char UNITY_PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char UNITY_PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; +const char UNITY_PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char UNITY_PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char UNITY_PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char UNITY_PROGMEM UnityStrBreaker[] = "-----------------------"; +static const char UNITY_PROGMEM UnityStrResultsTests[] = " Tests "; +static const char UNITY_PROGMEM UnityStrResultsFailures[] = " Failures "; +static const char UNITY_PROGMEM UnityStrResultsIgnored[] = " Ignored "; +#ifndef UNITY_EXCLUDE_DETAILS +static const char UNITY_PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char UNITY_PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; +#endif +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +/* Local helper function to print characters. */ +static void UnityPrintChar(const char *pch) +{ + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } +} + +/*-----------------------------------------------*/ +/* Local helper function to print ANSI escape strings e.g. "\033[42m". */ +#ifdef UNITY_OUTPUT_COLOR +static UNITY_UINT UnityPrintAnsiEscapeString(const char *string) +{ + const char *pch = string; + UNITY_UINT count = 0; + + while (*pch && (*pch != 'm')) + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + count++; + } + UNITY_OUTPUT_CHAR('m'); + count++; + + return count; +} +#endif + +/*-----------------------------------------------*/ +void UnityPrint(const char *string) +{ + const char *pch = string; + + if (pch != NULL) + { + while (*pch) + { +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + UnityPrintChar(pch); + pch++; + } + } +} +/*-----------------------------------------------*/ +void UnityPrintLen(const char *string, const UNITY_UINT32 length) +{ + const char *pch = string; + + if (pch != NULL) + { + while (*pch && ((UNITY_UINT32)(pch - string) < length)) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (style == UNITY_DISPLAY_STYLE_CHAR) + { + /* printable characters plus CR & LF are printed */ + UNITY_OUTPUT_CHAR('\''); + if ((number <= 126) && (number >= 32)) + { + UNITY_OUTPUT_CHAR((int)number); + } + /* write escaped carriage returns */ + else if (number == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (number == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 2); + } + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrintNumber(number); + } + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned((UNITY_UINT)number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (~number) + 1; + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + + if ((unsigned)nibbles > UNITY_MAX_NIBBLES) + { + nibbles = UNITY_MAX_NIBBLES; + } + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* + * This function prints a floating-point value in a format similar to + * printf("%.7g") on a single-precision machine or printf("%.9g") on a + * double-precision machine. The 7th digit won't always be totally correct + * in single-precision operation (for that level of accuracy, a more + * complicated algorithm would be needed). + */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ +#ifdef UNITY_INCLUDE_DOUBLE + static const int sig_digits = 9; + static const UNITY_INT32 min_scaled = 100000000; + static const UNITY_INT32 max_scaled = 1000000000; +#else + static const int sig_digits = 7; + static const UNITY_INT32 min_scaled = 1000000; + static const UNITY_INT32 max_scaled = 10000000; +#endif + + UNITY_DOUBLE number = input_number; + + /* print minus sign (does not handle negative zero) */ + if (number < 0.0f) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == 0.0f) + { + UnityPrint("0"); + } + else if (isnan(number)) + { + UnityPrint("nan"); + } + else if (isinf(number)) + { + UnityPrint("inf"); + } + else + { + UNITY_INT32 n_int = 0; + UNITY_INT32 n; + int exponent = 0; + int decimals; + int digits; + char buf[16] = {0}; + + /* + * Scale up or down by powers of 10. To minimize rounding error, + * start with a factor/divisor of 10^10, which is the largest + * power of 10 that can be represented exactly. Finally, compute + * (exactly) the remaining power of 10 and perform one more + * multiplication or division. + */ + if (number < 1.0f) + { + UNITY_DOUBLE factor = 1.0f; + + while (number < (UNITY_DOUBLE)max_scaled / 1e10f) + { + number *= 1e10f; + exponent -= 10; + } + while (number * factor < (UNITY_DOUBLE)min_scaled) + { + factor *= 10.0f; + exponent--; + } + + number *= factor; + } + else if (number > (UNITY_DOUBLE)max_scaled) + { + UNITY_DOUBLE divisor = 1.0f; + + while (number > (UNITY_DOUBLE)min_scaled * 1e10f) + { + number /= 1e10f; + exponent += 10; + } + while (number / divisor > (UNITY_DOUBLE)max_scaled) + { + divisor *= 10.0f; + exponent++; + } + + number /= divisor; + } + else + { + /* + * In this range, we can split off the integer part before + * doing any multiplications. This reduces rounding error by + * freeing up significant bits in the fractional part. + */ + UNITY_DOUBLE factor = 1.0f; + n_int = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n_int; + + while (n_int < min_scaled) + { + n_int *= 10; + factor *= 10.0f; + exponent--; + } + + number *= factor; + } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + /* round to even if exactly between two integers */ + if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) + n--; +#endif + + n += n_int; + + if (n >= max_scaled) + { + n = min_scaled; + exponent++; + } + + /* determine where to place decimal point */ + decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while ((decimals > 0) && ((n % 10) == 0)) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while ((n != 0) || (digits <= decimals)) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + + /* print out buffer (backwards) */ + while (digits > 0) + { + if (digits == decimals) + { + UNITY_OUTPUT_CHAR('.'); + } + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if (exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while ((exponent != 0) || (digits < 2)) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char *file, const UNITY_LINE_TYPE line) +{ +#ifdef UNITY_OUTPUT_FOR_ECLIPSE + UNITY_OUTPUT_CHAR('('); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(')'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else +#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH + UnityPrint("'); + UnityPrint(Unity.CurrentTestName); + UnityPrint(" "); +#else +#ifdef UNITY_OUTPUT_FOR_QT_CREATOR + UnityPrint("file://"); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#endif +#endif +#endif +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EXEC_TIME(); + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char *msg) +{ +#ifdef UNITY_PRINT_TEST_CONTEXT + UnityPrint(UnityStrSpacer); + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrSpacer); + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + } +#endif + if (msg) + { + UnityPrint(UnityStrSpacer); + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char *expected, const char *actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char *expected, + const char *actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char *msg) +{ + /* Both are NULL or same pointer */ + if (expected == actual) + { + return 0; + } + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) + { + return; + } + if ((threshold == actual)) + { + failed = 1; + } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) + { + failed = 1; + } + if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) + { + failed = 1; + } + } + else /* UINT or HEX */ + { + if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) + { + failed = 1; + } + if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) + { + failed = 1; + } + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) + { + UnityPrint(UnityStrGt); + } + if (compare & UNITY_SMALLER_THAN) + { + UnityPrint(UnityStrLt); + } + if (compare & UNITY_EQUAL_TO) + { + UnityPrint(UnityStrOrEqual); + } + if (compare == UNITY_NOT_EQUAL) + { + UnityPrint(UnityStrNotEqual); + } + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ + do \ + { \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; \ + } while (0) + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8 *)actual; + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x000000FF; + actual_val &= 0x000000FF; + } + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16 *)actual; + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x0000FFFF; + actual_val &= 0x0000FFFF; + } + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64 *)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32 *)actual; +#ifdef UNITY_SUPPORT_64 + if (style & (UNITY_DISPLAY_RANGE_UINT | UNITY_DISPLAY_RANGE_HEX)) + { + expect_val &= 0x00000000FFFFFFFF; + actual_val &= 0x00000000FFFFFFFF; + } +#endif + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if (expect_val != actual_val) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char *)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char *)actual + increment); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) \ + return 1; \ + if (UNITY_NAN_CHECK) \ + return 1; \ + (diff) = (actual) - (expected); \ + if ((diff) < 0) \ + (diff) = -(diff); \ + if ((delta) < 0) \ + (delta) = -(delta); \ + return !(isnan(diff) || isinf(diff) || ((diff) > (delta))) +/* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN +#define UNITY_NAN_CHECK isnan(expected) && isnan(actual) +#else +#define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + do \ + { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); \ + } while (0) +#else +#define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertWithinFloatArray(const UNITY_FLOAT delta, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *ptr_actual = actual; + UNITY_FLOAT in_delta = delta; + UNITY_FLOAT current_element_delta = delta; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (isinf(in_delta)) + { + return; /* Arrays will be force equal with infinite delta */ + } + + if (isnan(in_delta)) + { + /* Delta must be correct number */ + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + /* fix delta sign if need */ + if (in_delta < 0) + { + in_delta = -in_delta; + } + + while (elements--) + { + current_element_delta = *ptr_expected * UNITY_FLOAT_PRECISION; + + if (current_element_delta < 0) + { + /* fix delta sign for correct calculations */ + current_element_delta = -current_element_delta; + } + + if (!UnityFloatsWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat((UNITY_DOUBLE)expected); + UnityPrint(UnityStrNotEqual); + UnityPrintFloat((UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold, + const UNITY_FLOAT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + int failed; + + RETURN_IF_FAIL_OR_IGNORE; + + failed = 0; + + /* Checking for "not success" rather than failure to get the right result for NaN */ + if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) + { + failed = 1; + } + if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) + { + failed = 1; + } + + if ((compare & UNITY_EQUAL_TO) && UnityFloatsWithin(threshold * UNITY_FLOAT_PRECISION, threshold, actual)) + { + failed = 0; + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat(actual); + if (compare & UNITY_GREATER_THAN) + { + UnityPrint(UnityStrGt); + } + if (compare & UNITY_SMALLER_THAN) + { + UnityPrint(UnityStrLt); + } + if (compare & UNITY_EQUAL_TO) + { + UnityPrint(UnityStrOrEqual); + } + UnityPrintFloat(threshold); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char *trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *ptr_actual = actual; + UNITY_DOUBLE in_delta = delta; + UNITY_DOUBLE current_element_delta = delta; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (isinf(in_delta)) + { + return; /* Arrays will be force equal with infinite delta */ + } + + if (isnan(in_delta)) + { + /* Delta must be correct number */ + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + /* fix delta sign if need */ + if (in_delta < 0) + { + in_delta = -in_delta; + } + + while (elements--) + { + current_element_delta = *ptr_expected * UNITY_DOUBLE_PRECISION; + + if (current_element_delta < 0) + { + /* fix delta sign for correct calculations */ + current_element_delta = -current_element_delta; + } + + if (!UnityDoublesWithin(in_delta + current_element_delta, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat((UNITY_DOUBLE)expected); + UnityPrint(UnityStrNotEqual); + UnityPrintFloat((UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold, + const UNITY_DOUBLE actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + int failed; + + RETURN_IF_FAIL_OR_IGNORE; + + failed = 0; + + /* Checking for "not success" rather than failure to get the right result for NaN */ + if (!(actual < threshold) && (compare & UNITY_SMALLER_THAN)) + { + failed = 1; + } + if (!(actual > threshold) && (compare & UNITY_GREATER_THAN)) + { + failed = 1; + } + + if ((compare & UNITY_EQUAL_TO) && UnityDoublesWithin(threshold * UNITY_DOUBLE_PRECISION, threshold, actual)) + { + failed = 0; + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintFloat(actual); + if (compare & UNITY_GREATER_THAN) + { + UnityPrint(UnityStrGt); + } + if (compare & UNITY_SMALLER_THAN) + { + UnityPrint(UnityStrLt); + } + if (compare & UNITY_EQUAL_TO) + { + UnityPrint(UnityStrOrEqual); + } + UnityPrintFloat(threshold); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char *trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + else + { + if ((UNITY_UINT)actual > (UNITY_UINT)expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8 *)actual; + increment = sizeof(UNITY_INT8); + } + else + { + expect_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT8 *)expected; + actual_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT8 *)actual; + increment = sizeof(UNITY_UINT8); + } + break; + + case 2: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16 *)actual; + increment = sizeof(UNITY_INT16); + } + else + { + expect_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT16 *)expected; + actual_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT16 *)actual; + increment = sizeof(UNITY_UINT16); + } + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64 *)actual; + increment = sizeof(UNITY_INT64); + } + else + { + expect_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT64 *)expected; + actual_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT64 *)actual; + increment = sizeof(UNITY_UINT64); + } + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + /* fixing problems with signed overflow on unsigned numbers */ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32 *)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32 *)actual; + increment = sizeof(UNITY_INT32); + } + else + { + expect_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT32 *)expected; + actual_val = (UNITY_INT) * (UNITY_PTR_ATTRIBUTE const UNITY_UINT32 *)actual; + increment = sizeof(UNITY_UINT32); + } + length = 4; + break; + } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual_val > expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + else + { + if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char *)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char *)actual + increment); + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char *expected, + const char *actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char *expected, + const char *actual, + const UNITY_UINT32 length, + const char *msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char **actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char *expd = NULL; + const char *act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + + if ((const void *)expected == (const void *)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char *)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char *const *)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char *ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char *)expected; + UNITY_PTR_ATTRIBUTE const unsigned char *ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char *)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { +#ifdef UNITY_COMPARE_PTRS_ON_ZERO_ARRAY + UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, lineNumber, msg); +#else + UnityPrintPointlessAndBail(); +#endif + } + if (length == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char *)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch (size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED + +/*----------------------------------------------- + * printf length modifier helpers + *-----------------------------------------------*/ + +enum UnityLengthModifier +{ + UNITY_LENGTH_MODIFIER_NONE, + UNITY_LENGTH_MODIFIER_LONG_LONG, + UNITY_LENGTH_MODIFIER_LONG, +}; + +#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \ + do \ + { \ + switch (LENGTH_MOD) \ + { \ + case UNITY_LENGTH_MODIFIER_LONG_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_NONE: \ + default: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, ARG_T); \ + break; \ + } \ + } \ + } while (0) + +static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length) +{ + enum UnityLengthModifier length_mod; + switch (pch[0]) + { + case 'l': + { + if (pch[1] == 'l') + { + *length = 2; + length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG; + } + else + { + *length = 1; + length_mod = UNITY_LENGTH_MODIFIER_LONG; + } + break; + } + case 'h': + { + // short and char are converted to int + length_mod = UNITY_LENGTH_MODIFIER_NONE; + if (pch[1] == 'h') + { + *length = 2; + } + else + { + *length = 1; + } + break; + } + case 'j': + case 'z': + case 't': + case 'L': + { + // Not supported, but should gobble up the length specifier anyway + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 1; + break; + } + default: + { + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 0; + } + } + return length_mod; +} + +/*----------------------------------------------- + * printf helper function + *-----------------------------------------------*/ +static void UnityPrintFVA(const char *format, va_list va) +{ + const char *pch = format; + if (pch != NULL) + { + while (*pch) + { + /* format identification character */ + if (*pch == '%') + { + pch++; + + if (pch != NULL) + { + int length_mod_size; + enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size); + pch += length_mod_size; + + switch (*pch) + { + case 'd': + case 'i': + { + UNITY_INT number; + UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int); + UnityPrintNumber((UNITY_INT)number); + break; + } +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + case 'f': + case 'g': + { + const double number = va_arg(va, double); + UnityPrintFloat((UNITY_DOUBLE)number); + break; + } +#endif + case 'u': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UnityPrintNumberUnsigned(number); + break; + } + case 'b': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('b'); + UnityPrintMask(mask, number); + break; + } + case 'x': + case 'X': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex(number, 8); + break; + } + case 'p': + { + const unsigned int number = va_arg(va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 8); + break; + } + case 'c': + { + const int ch = va_arg(va, int); + UnityPrintChar((const char *)&ch); + break; + } + case 's': + { + const char *string = va_arg(va, const char *); + UnityPrint(string); + break; + } + case '%': + { + UnityPrintChar(pch); + break; + } + default: + { + /* print the unknown format character */ + UNITY_OUTPUT_CHAR('%'); + UnityPrintChar(pch); + break; + } + } + } + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + else if (*pch == '\n') + { + UNITY_PRINT_EOL(); + } + else + { + UnityPrintChar(pch); + } + + pch++; + } + } +} + +void UnityPrintF(const UNITY_LINE_TYPE line, const char *format, ...) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if (format != NULL) + { + UnityPrint(": "); + va_list va; + va_start(va, format); + UnityPrintFVA(format, va); + va_end(va); + } + UNITY_PRINT_EOL(); +} +#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityFail(const char *msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char *msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityMessage(const char *msg, const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_PRINT_EOL(); +} + +/*-----------------------------------------------*/ +/* If we have not defined our own test runner, then include our default test runner to make life easier */ +#ifndef UNITY_SKIP_DEFAULT_RUNNER +void UnityDefaultTestRun(UnityTestFunction Func, const char *FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} +#endif + +/*-----------------------------------------------*/ +void UnitySetTestFile(const char *filename) +{ + Unity.TestFile = filename; +} + +/*-----------------------------------------------*/ +void UnityBegin(const char *filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); + UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char *UnityOptionIncludeNamed = NULL; +char *UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; + +/*-----------------------------------------------*/ +int UnityParseOptions(int argc, char **argv) +{ + int i; + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + if (argv[i][2] == '=') + { + UnityOptionIncludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionIncludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + { + UnityOptionExcludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionExcludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + return 1; + } + } + } + + return 0; +} + +/*-----------------------------------------------*/ +int IsStringInBiggerString(const char *longstring, const char *shortstring) +{ + const char *lptr = longstring; + const char *sptr = shortstring; + const char *lnext = lptr; + + if (*sptr == '*') + { + return 1; + } + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + /* We're done if we match the entire string or up to a wildcard */ + if (*sptr == '*') + return 1; + if (*sptr == ',') + return 1; + if (*sptr == '"') + return 1; + if (*sptr == '\'') + return 1; + if (*sptr == ':') + return 2; + if (*sptr == 0) + return 1; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + + return 0; +} + +/*-----------------------------------------------*/ +int UnityStringArgumentMatches(const char *str) +{ + int retval; + const char *ptr1; + const char *ptr2; + const char *ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + { + ptr1++; + } + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + { + ptrf = &ptr2[1]; + } + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + { + ptr2++; + } + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + { + return retval; + } + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + { + return 1; + } + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + { + return 1; + } + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +/*-----------------------------------------------*/ +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + { + retval = 1; + } + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + { + retval = 0; + } + } + + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/lib/unity/unity.h b/lib/unity/unity.h new file mode 100644 index 0000000..e321a1d --- /dev/null +++ b/lib/unity/unity.h @@ -0,0 +1,687 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#define UNITY_VERSION_MAJOR 2 +#define UNITY_VERSION_MINOR 5 +#define UNITY_VERSION_BUILD 4 +#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. + * If using unity directly, these will need to be provided for each test + * executable built. If you are using the test runner generator and/or + * Ceedling, these are optional. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). If using + * Unity directly, you're in charge of calling these if they are desired. + * If using Ceedling or the test runner generator, these will be called + * automatically if they exist. */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/*------------------------------------------------------- + * Test Reset and Verify + *-------------------------------------------------------*/ + +/* These functions are intended to be called before during tests in order + * to support complex test loops, etc. Both are NOT built into Unity. Instead + * the test runner generator will create them. resetTest will run teardown and + * setup again, verifying any end-of-test needs between. verifyTest will only + * run the verification. */ +void resetTest(void); +void verifyTest(void); + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) and/or TEST_RANGE(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) +#define TEST_ONLY() +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__) +#endif + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() +#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0) + +/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out + * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ +#define TEST_FILE(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") +#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") +#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal") +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) +#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_FLOAT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN((delta), (expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_THAN_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_DOUBLE_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message) +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/unity/unity_config.h b/lib/unity/unity_config.h new file mode 100644 index 0000000..f246a51 --- /dev/null +++ b/lib/unity/unity_config.h @@ -0,0 +1,241 @@ +/* Unity Configuration + * As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529 + * Update: December 29th, 2016 + * See Also: Unity/docs/UnityConfigurationGuide.pdf + * + * Unity is designed to run on almost anything that is targeted by a C compiler. + * It would be awesome if this could be done with zero configuration. While + * there are some targets that come close to this dream, it is sadly not + * universal. It is likely that you are going to need at least a couple of the + * configuration options described in this document. + * + * All of Unity's configuration options are `#defines`. Most of these are simple + * definitions. A couple are macros with arguments. They live inside the + * unity_internals.h header file. We don't necessarily recommend opening that + * file unless you really need to. That file is proof that a cross-platform + * library is challenging to build. From a more positive perspective, it is also + * proof that a great deal of complexity can be centralized primarily to one + * place in order to provide a more consistent and simple experience elsewhere. + * + * Using These Options + * It doesn't matter if you're using a target-specific compiler and a simulator + * or a native compiler. In either case, you've got a couple choices for + * configuring these options: + * + * 1. Because these options are specified via C defines, you can pass most of + * these options to your compiler through command line compiler flags. Even + * if you're using an embedded target that forces you to use their + * overbearing IDE for all configuration, there will be a place somewhere in + * your project to configure defines for your compiler. + * 2. You can create a custom `unity_config.h` configuration file (present in + * your toolchain's search paths). In this file, you will list definitions + * and macros specific to your target. All you must do is define + * `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any + * further definitions it may need. + */ + +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +/* ************************* AUTOMATIC INTEGER TYPES *************************** + * C's concept of an integer varies from target to target. The C Standard has + * rules about the `int` matching the register size of the target + * microprocessor. It has rules about the `int` and how its size relates to + * other integer types. An `int` on one target might be 16 bits while on another + * target it might be 64. There are more specific types in compilers compliant + * with C99 or later, but that's certainly not every compiler you are likely to + * encounter. Therefore, Unity has a number of features for helping to adjust + * itself to match your required integer sizes. It starts off by trying to do it + * automatically. + **************************************************************************** */ + +/* The first attempt to guess your types is to check `limits.h`. Some compilers + * that don't support `stdint.h` could include `limits.h`. If you don't + * want Unity to check this file, define this to make it skip the inclusion. + * Unity looks at UINT_MAX & ULONG_MAX, which were available since C89. + */ +#define UNITY_EXCLUDE_LIMITS_H + +/* The second thing that Unity does to guess your types is check `stdint.h`. + * This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to + * learn about your system. It's possible you don't want it to do this or it's + * possible that your system doesn't support `stdint.h`. If that's the case, + * you're going to want to define this. That way, Unity will know to skip the + * inclusion of this file and you won't be left with a compiler error. + */ +/* #define UNITY_EXCLUDE_STDINT_H */ + +/* ********************** MANUAL INTEGER TYPE DEFINITION *********************** + * If you've disabled all of the automatic options above, you're going to have + * to do the configuration yourself. There are just a handful of defines that + * you are going to specify if you don't like the defaults. + **************************************************************************** */ + +/* Define this to be the number of bits an `int` takes up on your system. The + * default, if not auto-detected, is 32 bits. + * + * Example: + */ +/* #define UNITY_INT_WIDTH 16 */ + +/* Define this to be the number of bits a `long` takes up on your system. The + * default, if not autodetected, is 32 bits. This is used to figure out what + * kind of 64-bit support your system can handle. Does it need to specify a + * `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option + * is going to be ignored. + * + * Example: + */ +/* #define UNITY_LONG_WIDTH 16 */ + +/* Define this to be the number of bits a pointer takes up on your system. The + * default, if not autodetected, is 32-bits. If you're getting ugly compiler + * warnings about casting from pointers, this is the one to look at. + * + * Example: + */ +#define UNITY_POINTER_WIDTH 64 + +/* Unity will automatically include 64-bit support if it auto-detects it, or if + * your `int`, `long`, or pointer widths are greater than 32-bits. Define this + * to enable 64-bit support if none of the other options already did it for you. + * There can be a significant size and speed impact to enabling 64-bit support + * on small targets, so don't define it if you don't need it. + */ +/* #define UNITY_INCLUDE_64 */ + +/* *************************** FLOATING POINT TYPES **************************** + * In the embedded world, it's not uncommon for targets to have no support for + * floating point operations at all or to have support that is limited to only + * single precision. We are able to guess integer sizes on the fly because + * integers are always available in at least one size. Floating point, on the + * other hand, is sometimes not available at all. Trying to include `float.h` on + * these platforms would result in an error. This leaves manual configuration as + * the only option. + **************************************************************************** */ + +/* By default, Unity guesses that you will want single precision floating point + * support, but not double precision. It's easy to change either of these using + * the include and exclude options here. You may include neither, just float, + * or both, as suits your needs. + */ +#define UNITY_EXCLUDE_FLOAT +#define UNITY_INCLUDE_DOUBLE +/* #define UNITY_EXCLUDE_DOUBLE */ + +/* For features that are enabled, the following floating point options also + * become available. + */ + +/* Unity aims for as small of a footprint as possible and avoids most standard + * library calls (some embedded platforms don't have a standard library!). + * Because of this, its routines for printing integer values are minimalist and + * hand-coded. To keep Unity universal, though, we eventually chose to develop + * our own floating point print routines. Still, the display of floating point + * values during a failure are optional. By default, Unity will print the + * actual results of floating point assertion failures. So a failed assertion + * will produce a message like "Expected 4.0 Was 4.25". If you would like less + * verbose failure messages for floating point assertions, use this option to + * give a failure message `"Values Not Within Delta"` and trim the binary size. + */ +/* #define UNITY_EXCLUDE_FLOAT_PRINT */ + +/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C + * floats. If your compiler supports a specialty floating point type, you can + * always override this behavior by using this definition. + * + * Example: + */ +/* #define UNITY_FLOAT_TYPE float16_t */ + +/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard + * C doubles. If you would like to change this, you can specify something else + * by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long + * double` could enable gargantuan floating point types on your 64-bit processor + * instead of the standard `double`. + * + * Example: + */ +/* #define UNITY_DOUBLE_TYPE long double */ + +/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as + * documented in the Unity Assertion Guide, you will learn that they are not + * really asserting that two values are equal but rather that two values are + * "close enough" to equal. "Close enough" is controlled by these precision + * configuration options. If you are working with 32-bit floats and/or 64-bit + * doubles (the normal on most processors), you should have no need to change + * these options. They are both set to give you approximately 1 significant bit + * in either direction. The float precision is 0.00001 while the double is + * 10^-12. For further details on how this works, see the appendix of the Unity + * Assertion Guide. + * + * Example: + */ +/* #define UNITY_FLOAT_PRECISION 0.001f */ +/* #define UNITY_DOUBLE_PRECISION 0.001f */ + +/* *************************** MISCELLANEOUS *********************************** + * Miscellaneous configuration options for Unity + **************************************************************************** */ + +/* Unity uses the stddef.h header included in the C standard library for the + * "NULL" macro. Define this in order to disable the include of stddef.h. If you + * do this, you have to make sure to provide your own "NULL" definition. + */ +/* #define UNITY_EXCLUDE_STDDEF_H */ + +/* Define this to enable the unity formatted print macro: + * "TEST_PRINTF" + */ +/* #define UNITY_INCLUDE_PRINT_FORMATTED */ + +/* *************************** TOOLSET CUSTOMIZATION *************************** + * In addition to the options listed above, there are a number of other options + * which will come in handy to customize Unity's behavior for your specific + * toolchain. It is possible that you may not need to touch any of these but + * certain platforms, particularly those running in simulators, may need to jump + * through extra hoops to operate properly. These macros will help in those + * situations. + **************************************************************************** */ + +/* By default, Unity prints its results to `stdout` as it runs. This works + * perfectly fine in most situations where you are using a native compiler for + * testing. It works on some simulators as well so long as they have `stdout` + * routed back to the command line. There are times, however, where the + * simulator will lack support for dumping results or you will want to route + * results elsewhere for other reasons. In these cases, you should define the + * `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time + * (as an `int`, since this is the parameter type of the standard C `putchar` + * function most commonly used). You may replace this with whatever function + * call you like. + * + * Example: + * Say you are forced to run your test suite on an embedded processor with no + * `stdout` option. You decide to route your test result output to a custom + * serial `RS232_putc()` function you wrote like thus: + */ +/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ +/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ +/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ +/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ +/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ +/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ + +/* Some compilers require a custom attribute to be assigned to pointers, like + * `near` or `far`. In these cases, you can give Unity a safe default for these + * by defining this option with the attribute you would like. + * + * Example: + */ +/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */ +/* #define UNITY_PTR_ATTRIBUTE near */ + +/* Print execution time of each test when executed in verbose mode + * + * Example: + * + * TEST - PASS (10 ms) + */ +/* #define UNITY_INCLUDE_EXEC_TIME */ + +#endif /* UNITY_CONFIG_H */ diff --git a/lib/unity/unity_internals.h b/lib/unity/unity_internals.h new file mode 100644 index 0000000..6075b9a --- /dev/null +++ b/lib/unity/unity_internals.h @@ -0,0 +1,1181 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +#ifndef UNITY_EXCLUDE_STDDEF_H +#include +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#include +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits + * Attempt 2: UINTPTR_MAX in , or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +#if !defined(STM32) || defined(__clang__) +#define UNITY_FUNCTION_ATTR(a) __attribute__((a)) +#else +#define UNITY_FUNCTION_ATTR(a) /* ignore */ +#endif + +#ifndef UNITY_NORETURN +#if defined(__cplusplus) +#if __cplusplus >= 201103L +#define UNITY_NORETURN [[noreturn]] +#endif +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#if defined(_WIN32) && defined(_MSC_VER) +/* We are using MSVC compiler on Windows platform. */ +/* Not all Windows SDKs supports , but compiler can support C11: */ +/* https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/ */ +/* Not sure, that Mingw compilers has Windows SDK headers at all. */ +#include +#endif + +/* Using Windows SDK predefined macro for detecting supported SDK with MSVC compiler. */ +/* Mingw GCC should work without that fixes. */ +/* Based on: */ +/* https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 */ +/* NTDDI_WIN10_FE is equal to Windows 10 SDK 2104 */ +#if defined(_MSC_VER) && ((!defined(NTDDI_WIN10_FE)) || WDK_NTDDI_VERSION < NTDDI_WIN10_FE) +/* Based on tests and: */ +/* https://docs.microsoft.com/en-us/cpp/c-language/noreturn?view=msvc-170 */ +/* https://en.cppreference.com/w/c/language/_Noreturn */ +#define UNITY_NORETURN _Noreturn +#else /* Using newer Windows SDK or not MSVC compiler */ +#include +#define UNITY_NORETURN noreturn +#endif +#endif +#endif +#ifndef UNITY_NORETURN +#define UNITY_NORETURN UNITY_FUNCTION_ATTR(__noreturn__) +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Also sizeof(int) does return the size in addressable units on all platforms, + * which may not necessarily be the size in bytes. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH +#ifdef UINT_MAX +#if (UINT_MAX == 0xFFFF) +#define UNITY_INT_WIDTH (16) +#elif (UINT_MAX == 0xFFFFFFFF) +#define UNITY_INT_WIDTH (32) +#elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) +#define UNITY_INT_WIDTH (64) +#endif +#else /* Set to default */ +#define UNITY_INT_WIDTH (32) +#endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH +#ifdef ULONG_MAX +#if (ULONG_MAX == 0xFFFF) +#define UNITY_LONG_WIDTH (16) +#elif (ULONG_MAX == 0xFFFFFFFF) +#define UNITY_LONG_WIDTH (32) +#elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) +#define UNITY_LONG_WIDTH (64) +#endif +#else /* Set to default */ +#define UNITY_LONG_WIDTH (32) +#endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH +#ifdef UINTPTR_MAX +#if (UINTPTR_MAX <= 0xFFFF) +#define UNITY_POINTER_WIDTH (16) +#elif (UINTPTR_MAX <= 0xFFFFFFFF) +#define UNITY_POINTER_WIDTH (32) +#elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) +#define UNITY_POINTER_WIDTH (64) +#endif +#else /* Set to default */ +#define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH +#endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) +typedef unsigned char UNITY_UINT8; +typedef unsigned short UNITY_UINT16; +typedef unsigned int UNITY_UINT32; +typedef signed char UNITY_INT8; +typedef signed short UNITY_INT16; +typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) +typedef unsigned char UNITY_UINT8; +typedef unsigned int UNITY_UINT16; +typedef unsigned long UNITY_UINT32; +typedef signed char UNITY_INT8; +typedef signed int UNITY_INT16; +typedef signed long UNITY_INT32; +#else +#error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +/* Auto-detect 64 Bit Support */ +#ifndef UNITY_SUPPORT_64 +#if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 +#define UNITY_SUPPORT_64 +#endif +#endif + +/* 64-Bit Support Dependent Configuration */ +#ifndef UNITY_SUPPORT_64 +/* No 64-bit Support */ +typedef UNITY_UINT32 UNITY_UINT; +typedef UNITY_INT32 UNITY_INT; +#define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */ +#else +/* 64-bit Support */ +#if (UNITY_LONG_WIDTH == 32) +typedef unsigned long long UNITY_UINT64; +typedef signed long long UNITY_INT64; +#elif (UNITY_LONG_WIDTH == 64) +typedef unsigned long UNITY_UINT64; +typedef signed long UNITY_INT64; +#else +#error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) +#endif +typedef UNITY_UINT64 UNITY_UINT; +typedef UNITY_INT64 UNITY_INT; +#define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */ +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) +#define UNITY_PTR_TO_INT UNITY_INT32 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) +#define UNITY_PTR_TO_INT UNITY_INT64 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) +#define UNITY_PTR_TO_INT UNITY_INT16 +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else +#error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE +#define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR +#define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void * +#endif + +/* optionally define UNITY_COMPARE_PTRS_ON_ZERO_ARRAY */ + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isinf & isnan macros should be provided by math.h */ +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define isinf(n) (isnan((n) - (n)) && !isnan(n)) +#endif + +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE +#else +#undef UNITY_INCLUDE_DOUBLE +#endif + +#ifndef UNITY_EXCLUDE_FLOAT +#ifndef UNITY_DOUBLE_TYPE +#define UNITY_DOUBLE_TYPE double +#endif +typedef UNITY_FLOAT UNITY_DOUBLE; +/* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ +#endif + +#else + +/* Double Floating Point Support */ +#ifndef UNITY_DOUBLE_PRECISION +#define UNITY_DOUBLE_PRECISION (1e-12) +#endif + +#ifndef UNITY_DOUBLE_TYPE +#define UNITY_DOUBLE_TYPE double +#endif +typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR +/* Default to using putchar, which is defined in stdio.h */ +#include +#define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else +/* If defined as something else, make sure we declare it here so it's ready for use */ +#ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION +extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; +#endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#ifdef UNITY_USE_FLUSH_STDOUT +/* We want to use the stdout flush utility */ +#include +#define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) +#else +/* We've specified nothing, therefore flush should just be ignored */ +#define UNITY_OUTPUT_FLUSH() (void)0 +#endif +#else +/* If defined as something else, make sure we declare it here so it's ready for use */ +#ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION +extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION; +#endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +#ifdef UNITY_INCLUDE_EXEC_TIME +#if !defined(UNITY_EXEC_TIME_START) && \ + !defined(UNITY_EXEC_TIME_STOP) && \ + !defined(UNITY_PRINT_EXEC_TIME) && \ + !defined(UNITY_TIME_TYPE) +/* If none any of these macros are defined then try to provide a default implementation */ + +#if defined(UNITY_CLOCK_MS) +/* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */ +#define UNITY_TIME_TYPE UNITY_UINT +#define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS() +#define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS() +#define UNITY_PRINT_EXEC_TIME() \ + { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } +#elif defined(_WIN32) +#include +#define UNITY_TIME_TYPE clock_t +#define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC) +#define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) +#define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) +#define UNITY_PRINT_EXEC_TIME() \ + { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } +#elif defined(__unix__) || defined(__APPLE__) +#include +#define UNITY_TIME_TYPE struct timespec +#define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) +#define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) +#define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) +#define UNITY_PRINT_EXEC_TIME() \ + { \ + UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \ + execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } +#endif +#endif +#endif + +#ifndef UNITY_EXEC_TIME_START +#define UNITY_EXEC_TIME_START() \ + do \ + { /* nothing*/ \ + } while (0) +#endif + +#ifndef UNITY_EXEC_TIME_STOP +#define UNITY_EXEC_TIME_STOP() \ + do \ + { /* nothing*/ \ + } while (0) +#endif + +#ifndef UNITY_TIME_TYPE +#define UNITY_TIME_TYPE UNITY_UINT +#endif + +#ifndef UNITY_PRINT_EXEC_TIME +#define UNITY_PRINT_EXEC_TIME() \ + do \ + { /* nothing*/ \ + } while (0) +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) +#define UNITY_DISPLAY_RANGE_CHAR (0x80) + +typedef enum +{ + UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + + UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT, + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_WITHIN = 0x0, + UNITY_EQUAL_TO = 0x1, + UNITY_GREATER_THAN = 0x2, + UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 0x4, + UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, + UNITY_NOT_EQUAL = 0x0, + UNITY_UNKNOWN +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY, + UNITY_ARRAY_UNKNOWN +} UNITY_FLAGS_T; + +struct UNITY_STORAGE_T +{ + const char *TestFile; + const char *CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char *CurrentDetail1; + const char *CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifdef UNITY_INCLUDE_EXEC_TIME + UNITY_TIME_TYPE CurrentTestStartTime; + UNITY_TIME_TYPE CurrentTestStopTime; +#endif +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char *filename); +int UnityEnd(void); +void UnitySetTestFile(const char *filename); +void UnityConcludeTest(void); + +#ifndef RUN_TEST +void UnityDefaultTestRun(UnityTestFunction Func, const char *FuncName, const int FuncLineNum); +#else +#define UNITY_SKIP_DEFAULT_RUNNER +#endif + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1, d2) +#else +#define UNITY_CLR_DETAILS() \ + do \ + { \ + Unity.CurrentDetail1 = 0; \ + Unity.CurrentDetail2 = 0; \ + } while (0) +#define UNITY_SET_DETAIL(d1) \ + do \ + { \ + Unity.CurrentDetail1 = (d1); \ + Unity.CurrentDetail2 = 0; \ + } while (0) +#define UNITY_SET_DETAILS(d1, d2) \ + do \ + { \ + Unity.CurrentDetail1 = (d1); \ + Unity.CurrentDetail2 = (d2); \ + } while (0) + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +#ifdef UNITY_PRINT_TEST_CONTEXT +void UNITY_PRINT_TEST_CONTEXT(void); +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char *string); + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +void UnityPrintF(const UNITY_LINE_TYPE line, const char *format, ...); +#endif + +void UnityPrintLen(const char *string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number_to_print); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char *expected, + const char *actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char *expected, + const char *actual, + const UNITY_UINT32 length, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char **actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +#ifndef UNITY_EXCLUDE_SETJMP_H +UNITY_NORETURN void UnityFail(const char *message, const UNITY_LINE_TYPE line); +UNITY_NORETURN void UnityIgnore(const char *message, const UNITY_LINE_TYPE line); +#else +void UnityFail(const char *message, const UNITY_LINE_TYPE line); +void UnityIgnore(const char *message, const UNITY_LINE_TYPE line); +#endif + +void UnityMessage(const char *message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertFloatsNotWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertGreaterOrLessFloat(const UNITY_FLOAT threshold, + const UNITY_FLOAT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE linenumber); + +void UnityAssertWithinFloatArray(const UNITY_FLOAT delta, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT *actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertDoublesNotWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertGreaterOrLessDouble(const UNITY_DOUBLE threshold, + const UNITY_DOUBLE actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE linenumber); + +void UnityAssertWithinDoubleArray(const UNITY_DOUBLE delta, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE *actual, + const UNITY_UINT32 num_elements, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrOk[]; +extern const char UnityStrPass[]; +extern const char UnityStrFail[]; +extern const char UnityStrIgnore[]; + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; +extern const char UnityStrErrShorthand[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_PROTECT() 1 +#define TEST_ABORT() return +#endif + +/* Automatically enable variadic macros support, if it not enabled before */ +#ifndef UNITY_SUPPORT_VARIADIC_MACROS +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define UNITY_SUPPORT_VARIADIC_MACROS +#endif +#endif +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef UNITY_SUPPORT_VARIADIC_MACROS +#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) +#endif +#endif + +/* Enable default macros for masking param tests test cases */ +#ifdef UNITY_SUPPORT_TEST_CASES +#ifdef UNITY_SUPPORT_VARIADIC_MACROS +#if !defined(TEST_CASE) && !defined(UNITY_EXCLUDE_TEST_CASE) +#define TEST_CASE(...) +#endif +#if !defined(TEST_RANGE) && !defined(UNITY_EXCLUDE_TEST_RANGE) +#define TEST_RANGE(...) +#endif +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +#ifndef UNITY_SHORTHAND_AS_INT +#ifndef UNITY_SHORTHAND_AS_MEM +#ifndef UNITY_SHORTHAND_AS_NONE +#ifndef UNITY_SHORTHAND_AS_RAW +#define UNITY_SHORTHAND_AS_OLD +#endif +#endif +#endif +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char **argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail((message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore((message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) \ + do \ + { \ + if (condition) \ + { /* nothing*/ \ + } \ + else \ + { \ + UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); \ + } \ + } while (0) +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8)(expected), (UNITY_INT)(UNITY_INT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8)(expected), (UNITY_INT)(UNITY_UINT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8)(expected), (UNITY_INT)(UNITY_INT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8)(expected), (UNITY_INT)(UNITY_INT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8)(threshold), (UNITY_INT)(UNITY_UINT8)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8)(threshold), (UNITY_INT)(UNITY_INT8)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8)(delta), (UNITY_INT)(UNITY_INT8)(expected), (UNITY_INT)(UNITY_INT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8)(delta), (UNITY_INT)(UNITY_INT8)(expected), (UNITY_INT)(UNITY_INT8)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char *)(expected), (const char *)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char *)(expected), (const char *)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char **)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8)(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8)(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8)(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT)(expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char **)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8)(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsNotWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_NOT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_FLOAT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)(delta), (const UNITY_FLOAT *)(expected), (const UNITY_FLOAT *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, (const UNITY_FLOAT *)(expected), (const UNITY_FLOAT *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertWithinFloatArray((UNITY_FLOAT)0, UnityFloatToPtr(expected), (const UNITY_FLOAT *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_FLOAT(threshold, actual, line, message) UnityAssertGreaterOrLessFloat((UNITY_FLOAT)(threshold), (UNITY_FLOAT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesNotWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_NOT_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_DOUBLE_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)(delta), (const UNITY_DOUBLE *)(expected), (const UNITY_DOUBLE *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, (const UNITY_DOUBLE *)(expected), (const UNITY_DOUBLE *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertWithinDoubleArray((UNITY_DOUBLE)0, UnityDoubleToPtr(expected), (const UNITY_DOUBLE *)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_LESS_OR_EQUAL_DOUBLE(threshold, actual, line, message) UnityAssertGreaterOrLessDouble((UNITY_DOUBLE)(threshold), (UNITY_DOUBLE)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..65bff25 --- /dev/null +++ b/readme.txt @@ -0,0 +1,4 @@ +1,delay文件夹:存放延时相关的驱动代码。 +2,sys文件夹:存放系统相关驱动代码。 +3,uart文件夹:存放串口相关代码。 +4,rt-thread:https://zhuanlan.zhihu.com/p/653746487?utm_campaign=shareopn&utm_medium=social&utm_psn=1827650748781039617&utm_source=wechat_session diff --git a/src/btn.c b/src/btn.c new file mode 100644 index 0000000..a07c22d --- /dev/null +++ b/src/btn.c @@ -0,0 +1,248 @@ +/* + * @Author: + * @Date: 2023-07-04 08:25:56 + * @LastEditors: xxx + * @LastEditTime: 2023-08-25 11:13:52 + * @Description:一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰 + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "../inc/btn.h" + +#define EVENT_CB(ev) \ + if (handle->cb[ev]) \ + handle->cb[ev]((Button *)handle) + +// button handle list head. +static struct Button *head_handle = NULL; + +/** + * @brief 初始化按钮结构体句柄。 + * @param handle: 按钮句柄结构体。 + * @param pin_level: 读取按钮连接的HAL GPIOLevel。 + * @param active_level: 按下按钮的GPIOLevel。 + * @param button_id: 按钮ID。 + * @retval 无 + */ +void button_init(struct Button *handle, uint8_t (*pin_level)(uint8_t), active_level_e active_level, uint8_t button_id, uint8_t button_id_reverse) +{ +#ifdef STM32 + osel_memset((uint8_t *)handle, 0, sizeof(struct Button)); +#else + memset(handle, 0, sizeof(struct Button)); +#endif + + handle->event = (uint8_t)NONE_PRESS; + handle->hal_button_Level = pin_level; + handle->button_level = handle->hal_button_Level(button_id); + handle->active_level = (uint8_t)active_level; + handle->button_id = button_id; + handle->button_id_reverse = button_id_reverse; +} + +/** + * @brief 为按钮添加事件回调函数。 + * @param handle: 按钮句柄结构体。 + * @param event: 触发事件类型。 + * @param cb: 回调函数。 + * @retval 无 + */ +void button_attach(struct Button *handle, PressEvent event, BtnCallback cb) +{ + handle->cb[event] = cb; +} + +/** + * @brief 查询按钮发生的事件。 + * @param handle: 按钮句柄结构体。 + * @retval 按钮事件。 + */ +PressEvent get_button_event(struct Button *handle) +{ + return (PressEvent)(handle->event); +} + +/** + * @brief 按钮驱动核心函数,驱动状态机。 + * @param handle: 按钮句柄结构体。 + * @retval 无 + */ +void button_handler(struct Button *handle) +{ + uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id); + + // ticks counter working.. + if ((handle->state) > 0) + handle->ticks++; + + /*------------button debounce handle---------------*/ + if (read_gpio_level != handle->button_level) + { // not equal to prev one + // continue read 3 times same new level change + if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) + { + handle->button_level = read_gpio_level; + handle->debounce_cnt = 0; + } + } + else + { // leved not change ,counter reset. + handle->debounce_cnt = 0; + } + + /*-----------------State machine-------------------*/ + switch (handle->state) + { + case 0: + if (handle->button_level == handle->active_level) + { // start press down + handle->event = (uint8_t)PRESS_DOWN; + EVENT_CB(PRESS_DOWN); + handle->ticks = 0; + handle->repeat = 1; + handle->state = 1; + } + else + { + handle->event = (uint8_t)NONE_PRESS; + } + break; + + case 1: + if (handle->button_level != handle->active_level) + { // released press up + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + handle->ticks = 0; + handle->state = 2; + } + else if (handle->ticks > LONG_TICKS) + { + handle->event = (uint8_t)LONG_PRESS_START; + EVENT_CB(LONG_PRESS_START); + handle->state = 5; + } + break; + + case 2: + if (handle->button_level == handle->active_level) + { // press down again + handle->event = (uint8_t)PRESS_DOWN; + EVENT_CB(PRESS_DOWN); + handle->repeat++; + EVENT_CB(PRESS_REPEAT); // repeat hit + handle->ticks = 0; + handle->state = 3; + } + else if (handle->ticks > SHORT_TICKS) + { // released timeout + if (handle->repeat == 1) + { + handle->event = (uint8_t)SINGLE_CLICK; + EVENT_CB(SINGLE_CLICK); + } + else if (handle->repeat == 2) + { + handle->event = (uint8_t)DOUBLE_CLICK; + EVENT_CB(DOUBLE_CLICK); // repeat hit + } + handle->state = 0; + } + break; + + case 3: + if (handle->button_level != handle->active_level) + { // released press up + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + if (handle->ticks < SHORT_TICKS) + { + handle->ticks = 0; + handle->state = 2; // repeat press + } + else + { + handle->state = 0; + } + } + else if (handle->ticks > SHORT_TICKS) + { // long press up + handle->state = 0; + } + break; + + case 5: + if (handle->button_level == handle->active_level) + { + // continue hold trigger + handle->event = (uint8_t)LONG_PRESS_HOLD; + EVENT_CB(LONG_PRESS_HOLD); + } + else + { // releasd + handle->event = (uint8_t)PRESS_UP; + EVENT_CB(PRESS_UP); + handle->state = 0; // reset + } + break; + default: + handle->state = 0; // reset + break; + } +} + +/** + * @brief 启动按钮工作,将句柄添加到工作队列中。 + * @param handle: 目标句柄结构体。 + * @retval 0: 成功。-1: 已存在。 + */ +int button_start(struct Button *handle) +{ + struct Button *target = head_handle; + while (target) + { + if (target == handle) + return -1; // already exist. + target = target->next; + } + handle->next = head_handle; + head_handle = handle; + return 0; +} + +/** + * @brief 停止按钮工作,从工作队列中移除句柄。 + * @param handle: 目标句柄结构体。 + * @retval None + */ +void button_stop(struct Button *handle) +{ + struct Button **curr; + for (curr = &head_handle; *curr;) + { + struct Button *entry = *curr; + if (entry == handle) + { + *curr = entry->next; + // free(entry); + return; + } + else + curr = &entry->next; + } +} + +/** + * @brief 后台计时,定时器重复调用间隔为5ms。 + * @param None. + * @retval None + */ +void button_ticks() +{ + struct Button *target; + for (target = head_handle; target; target = target->next) + { + button_handler(target); + } +} diff --git a/src/delay.c b/src/delay.c new file mode 100644 index 0000000..e39df7d --- /dev/null +++ b/src/delay.c @@ -0,0 +1,116 @@ +/* + * @Author: + * @Date: 2023-04-11 18:31:07 + * @LastEditors: xxx + * @LastEditTime: 2023-08-25 11:27:12 + * @Description: + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "delay.h" +// static uint16_t g_fac_ms = 0; // ms延时倍乘数,在os下,代表每个节拍的ms数 +static uint32_t g_fac_us = 0; /* us延时倍乘数 */ + +void SysTick_Init(void) +{ + NVIC_SetPriority(SysTick_IRQn, 3); + LL_SYSTICK_EnableIT(); +} +/** + * @brief 初始化延迟函数 + * @param sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck) + * @retval 无 + */ +void delay_init(uint16_t sysclk) +{ +#if SYS_SUPPORT_OS /* 如果需要支持OS */ + uint32_t reload; +#endif + // SysTick_Init(); + // LL_SetSystemCoreClock(LL_SYSTICK_CLKSOURCE_HCLK); /* SYSTICK使用内核时钟源,同CPU同频率 */ + g_fac_us = sysclk; /* 不论是否使用OS,g_fac_us都需要使用 */ + // Remove the redundant assignment statement +#if SYS_SUPPORT_OS /* 如果需要支持OS. */ + reload = sysclk; /* 每秒钟的计数次数 单位为M */ + reload *= 1000000 / configTICK_RATE_HZ; /* 根据delay_ostickspersec设定溢出时间,reload为24位 + * 寄存器,最大值:16777216,在168M下,约合0.099s左右 + */ + g_fac_ms = 1000 / configTICK_RATE_HZ; // 代表OS可以延时的最少单位 + SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启SYSTICK中断 */ + SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */ + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启SYSTICK */ +#endif +} + +/** + * @brief 延时nus + * @param nus: 要延时的us数. + * @note 注意: nus的值,不要大于34952us(最大值即2^24 / g_fac_us @g_fac_us = 168) + * @retval 无 + */ +void delay_us(uint32_t nus) +{ + uint32_t ticks; + uint32_t told, tnow, tcnt = 0; + uint32_t reload = SysTick->LOAD; /* LOAD的值 */ + ticks = nus * g_fac_us; /* 需要的节拍数 */ + told = SysTick->VAL; /* 刚进入时的计数器值 */ + while (1) + { + tnow = SysTick->VAL; + if (tnow != told) + { + if (tnow < told) + { + tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */ + } + else + { + tcnt += reload - tnow + told; + } + told = tnow; + if (tcnt >= ticks) + { + // __NOP(); + break; /* 时间超过/等于要延迟的时间,则退出 */ + } + } + } +} + +/** + * @brief 延时nms + * @param nms: 要延时的ms数 (0< nms <= 65535) + * @retval 无 + */ +void delay_ms(uint16_t nms) +{ + uint32_t repeat = nms / 30; /* 这里用30,是考虑到可能有超频应用 */ + uint32_t remain = nms % 30; + + while (repeat) + { + delay_us(30 * 1000); /* 利用delay_us 实现 1000ms 延时 */ + repeat--; + } + + if (remain) + { + delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */ + } +} + +/** + * @brief 延时函数,用于模拟硬件延时。 + * @param {uint32_t} ticks + * @return {*} + * @note: 请注意,这个函数仅用于模拟硬件延时,实际应用中可能需要使用其他延时函数,如HAL_Delay或rt_delay。 + */ +void delay_tick(uint32_t ticks) +{ + while (ticks--) + { + __NOP(); + } +} diff --git a/src/sys.c b/src/sys.c new file mode 100644 index 0000000..01f1284 --- /dev/null +++ b/src/sys.c @@ -0,0 +1,274 @@ +/* + * @Author: + * @Date: 2023-04-11 18:46:58 + * @LastEditors: xxx + * @LastEditTime: 2023-08-25 11:31:06 + * @Description: + * email: + * Copyright (c) 2023 by xxx, All Rights Reserved. + */ + +#include "sys.h" +#include "delay.h" +clock_config_t original_clock_config; // 原始时钟配置 +__IO uint32_t uw_tick; +__IO uint32_t scheduler_start_time; // 调度器开始时间 +__IO uint32_t scheduler_end_time; // 调度器结束时间 +__IO uint32_t scheduler_occupancy_time = 0; // 调度器占用时间 +/** + * @brief 设置中断向量表偏移地址 + * @param baseaddr : 基址 + * @param offset : 偏移量 + * @retval 无 + */ +void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset) +{ + /* 设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留 */ + SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00); +} + +/** + * @brief 执行: WFI指令(执行完该指令进入低功耗状态, 等待中断唤醒) + * @param 无 + * @retval 无 + */ +void sys_wfi_set(void) +{ + __ASM __IO("wfi"); +} + +/** + * @brief 关闭所有中断(但是不包括fault和NMI中断) + * @param 无 + * @retval 无 + */ +void sys_intx_disable(void) +{ + __ASM __IO("cpsid i"); +} + +/** + * @brief 开启所有中断 + * @param 无 + * @retval 无 + */ +void sys_intx_enable(void) +{ + __ASM __IO("cpsie i"); +} + +/** + * @brief 设置栈顶地址 + * @note 左侧若有红X, 属于MDK误报, 实际是没问题的 + * @param addr: 栈顶地址 + * @retval 无 + */ +void sys_msr_msp(uint32_t addr) +{ + __set_MSP(addr); /* 设置栈顶地址 */ +} + +/** + * @brief 进入待机模式 + * @param 无 + * @retval 无 + */ +void sys_standby(void) +{ + // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); /* 使能电源时钟 */ + // SET_BIT(PWR->CR, PWR_CR_PDDS); /* 进入待机模式 */ +} + +/** + * @brief 系统软复位 + * @param 无 + * @retval 无 + */ +void sys_soft_reset(void) +{ + NVIC_SystemReset(); +} + +/** + * @brief 10ms滴答定时器,需要单独放到定时器中断中执行 + * @param 无 + * @retval 提供给sys_millis()函数使用,之前放在SysTick_Handler中执行影响精度 + */ +__weak void LL_IncTick(void) +{ + uw_tick += 1; +} + +/** + * @brief 获取系统当前滴答计数 + * @param 无 + * @retval 当前滴答计数 + */ +uint32_t sys_get_tick(void) +{ + return uw_tick; +} + +/** + * @brief 获取系统当前毫秒级时间戳。 + * @return {uint32_t} 当前毫秒级时间戳 + * @note: 请注意,这个函数仅用于模拟硬件延时,实际应用中可能需要使用其他时钟源,如RTC或外部时钟。 + */ +uint32_t sys_millis(void) +{ + return uw_tick * 10; +} + +/** + * @brief 系统计时器重新开始 + * @return {*} + * @note + */ +void sys_millis_reset(void) +{ + uw_tick = 0; +} + +/** + * @brief 将系统时间戳转换为秒级时间戳。 + * @param {uint32_t} start_time 开始时间戳 + * @return {uint32_t} 秒级时间戳 + * @note: 请注意,这个函数仅用于模拟硬件延时,实际应用中可能需要使用其他时钟源,如RTC或外部时钟。 + */ +uint32_t sys_to_seconds(uint32_t start_time) +{ + return (sys_millis() - start_time) / 1000; +} + +/** + * @brief 记录调度器开始时间 + * @return {*} + * @note + */ +void scheduler_time_start(void) +{ + scheduler_start_time = sys_millis(); +} + +/** + * @brief 返回调度器运行时间 + * @return {*} + * @note + */ +uint32_t scheduler_time_stop(void) +{ + uint32_t scheduler_end_time = sys_millis() - scheduler_start_time; + scheduler_occupancy_time += scheduler_end_time; + return scheduler_end_time; +} + +/** + * @brief 计算任务占用时间百分比 + * @param {uint32_t} run_time 运行时间 + * @return {*} 任务占用时间百分比,单位为% + * @note + */ +uint32_t scheduler_time_occupancy_get(uint32_t run_time) +{ + float32 percent = 0.0f; + percent = (float32)(scheduler_occupancy_time) / (float32)run_time; + scheduler_occupancy_time = 0; + return (uint32_t)(percent * 100); +} + +// 切换到新的时钟配置 +void change_system_clock(clock_config_t *new_config) +{ +#if CLOCK_CHANGE_ENABLE == TRUE + // 1. 切换到HSE作为临时时钟源,根据你的硬件配置来调整 + LL_RCC_HSE_Enable(); + while (LL_RCC_HSE_IsReady() != 1) + { + } + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) + { + } + + // 2. 关闭PLL + LL_RCC_PLL_Disable(); + while (LL_RCC_PLL_IsReady() != 0) + { + } + + // 3. 配置新的PLL参数 + // 注意:这里假设HSE作为PLL的时钟源,你需要根据你的硬件配置来调整 + LL_RCC_PLL_ConfigDomain_SYS(new_config->pll_source, new_config->pll_m, new_config->pll_n, new_config->pll_r); + + // 4. 重新启用PLL + LL_RCC_PLL_EnableDomain_SYS(); + LL_RCC_PLL_Enable(); + while (LL_RCC_PLL_IsReady() != 1) + { + } + + // 5. 切换回PLL作为系统时钟源 + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) + { + } + + // 6. 更新时钟分频器 + LL_RCC_SetAHBPrescaler(new_config->ahb_div); + LL_RCC_SetAPB1Prescaler(new_config->apb1_div); + LL_RCC_SetAPB2Prescaler(new_config->apb2_div); + + // 7. 更新SystemCoreClock变量 + LL_Init1msTick(new_config->sysclk); + LL_SetSystemCoreClock(new_config->sysclk); + original_clock_config.sysclk_change = new_config->sysclk; + + delay_init((SystemCoreClock / 1000000)); +#endif +} + +// 恢复到原始时钟配置 +void restore_system_clock(void) +{ +#if CLOCK_CHANGE_ENABLE == TRUE + change_system_clock(&original_clock_config); +#endif +} + +// 获取原始时钟配置 +clock_config_t *get_original_clock_config(void) +{ + return &original_clock_config; +} + +// 在系统启动时调用此函数来初始化时钟并保存原始配置 +void system_clock_read(void) +{ +#if CLOCK_CHANGE_ENABLE == TRUE + // 保存原始时钟配置 + original_clock_config.pll_source = LL_RCC_PLL_GetMainSource(); + original_clock_config.pll_m = LL_RCC_PLL_GetDivider(); + original_clock_config.pll_n = LL_RCC_PLL_GetN(); + original_clock_config.pll_r = LL_RCC_PLL_GetR(); + + original_clock_config.ahb_div = LL_RCC_GetAHBPrescaler(); + original_clock_config.apb1_div = LL_RCC_GetAPB1Prescaler(); + original_clock_config.apb2_div = LL_RCC_GetAPB2Prescaler(); + original_clock_config.sysclk = SystemCoreClock; +#endif +} + +/** + * @brief Write a character to a file stream, used for FLASHDB printf + * + * Writes the specified character to the given file stream and returns the written character. + * + * @param ch The character to be written + * @param stream Pointer to the file stream + * + * @return The written character + */ +int fputc(int ch, FILE *stream) +{ + return ch; +}