motor_f103/User/lib/src/lib.c

827 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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 <stdio.h>
#include <string.h>
#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;
}
*/
/**************************线性拟合**************************/