写在正文之前
在书写代码的时候需要注意代码的规范,不规范的代码运行起来容易出现更多的问题。
同时,在解决一些问题的时候,需要设计一套完整的算法才能完美地解决问题。
不论是代码的规范还是解决问题的算法,都不是一蹴而就的,靠的是平常一点一滴的积累,在这里我希望大家多进行积累这件事,也许在不远的将来可以给你带来很多收获!
ps:虽然本文代码全部都是基于蓝桥杯嵌入式平台给出的,但是其中的真意都是相通的
正文部分
代码规范
信号处理代码
当需要设置占空比和自动重装载值的时候,我们不能时时刻刻都去改变,这是一种对资源的浪费,所以应该设置校验位
这里有一段处理信号的代码,当然这里的代码也存在问题
接下来我会说明这段代码存在的问题并给出处理之后的代码
存在问题的代码
void SIG_Proc(void)
{
uint16_t frq_tmp = 0, pwm_tmp = 0;
if((frq != frq_tmp) || (pwm_tmp != pwm)){
frq_tmp = frq;
pwm_tmp = pwm;
LL_TIM_SetAutoReload(TIM3, 1000000/frq - 1);
LL_TIM_OC_SetCompareCH2(TIM3, (1000000/frq + 1)*pwm/100);
}
VR37 = ADC2_Read()*3.3/4095;
}
关于这段代码,存在两个问题
问题一
在调用这个函数的时候临时创建两个变量,这个地方写的不好,应该定义一个全局变量
由于生存期和作用域的关系,每次调用这个函数都会把变量重新定义置零
所以其实根本就没有达到它想实现的只有频率和占空比改变的时候才重新设置
问题二
函数库问题,这里用的是LL库,而你使用的是HAL库
所以需要对函数进行修改改成HAL库代码
修改后的代码
将上述的问题解决之后,这里的代码就是一段比较理想的信号处理代码了
uint frq_temp=0;
uint duty_temp=0;
void Sig_Proc(void)
{
if(frq_temp!=frq||duty_temp!=duty)
{
frq_temp=frq;
duty_temp=duty;
__HAL_TIM_SET_AUTORELOAD(&htim17,1000000/frq-1);
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,(1000000/frq+1)*duty/100);
HAL_TIM_GenerateEvent(&htim17,TIM_EventSource_Update);
}
}
串口接收处理代码
当接收一个字符才为正确情况时
直接用switch语句能大量节省代码语句,只需要switch出符合的字符,其他的直接一个default执行接收错误的操作
需要注意的是,switch case能传进来的参数的类型是有要求的,注意一下
还是用if条件语句吧
关于串口接收的代码
if(rx_pointer!=0)
{
uchar temp=rx_pointer;
HAL_Delay(1);
if(rx_pointer==temp)
{
usart_Proc();
rx_pointer=0;
memset(rx_data,0,sizeof(rx_data));
}
}
注意在这里面写的usart_Proc函数需要再写一个memset清除接收数组里面的数据,尽管每次的接收都会给存储信息的数组重新赋值,但是如果上次接收的数据比这次接收的数据要多,那接收到的信息就会存在错误
PWM频率和占空比设置代码
__HAL_TIM_SET_AUTORELOAD(&htim17,1000000/frq-1);
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,(1000000/frq+1)*duty/100);
HAL_TIM_GenerateEvent(&htim17,TIM_EventSource_Update);
这样写出来的代码的占空比和频率完全正确,要记住这个公式
以后设置频率占空比就都用这个来改变了
但是这段代码放在信号处理代码里面的
处理问题的算法
年月转换成日
这看上去是一个很容易实现的功能,也许很多人在最初学习C语言的时候就已经写过了相关的算法内容
解决问题的难点
这个问题的难点在于当时间跨度足够长的时候,增加了很多额外的判断
每隔4年有一个闰年,每个闰年多一天
但是每隔100年有一个世纪年,它不是闰年(除非能被400整除)
每隔400年有一个特殊的世纪年,它又是闰年
解决的办法
解决的办法其实很简单,就是将所有的时间都转换成天数再进行计算,之所以这样做,是因为闰年和平年存在差异,每个月份之间也存在差异,而日是不存在差异的
将年转换成日
uint32_t days_in_year = car_yea1 * 365 + (car_yea1 / 4) - (car_yea1 / 100) + (car_yea1 / 400);
说明
这个公式是用于将年份转换为天数的近似公式,它考虑了闰年和平年的情况。
每个平年有365天,所以首先将年份乘以365。
每隔4年有一个闰年,因此 (car_yea1 / 4) 表示car_yea1年中的闰年数,每个闰年多一天,所以将其加到总天数中)
但是每隔100年有一个世纪年,它不是闰年(除非能被400整除),因此 (car_yea1 / 100) 表示car_yea1年中的世纪年数,需要将这些世纪减去,以避免重复计算
每隔400年有一个特殊的世纪年,它又是闰年,所以 (car_yea1 / 400) 表示car_yea1年中的特殊世纪数,需要将其加到总天数中。
将月转换成日
使用一个二维数组来存储每个月份在平年和闰年中的天数,然后根据年份是否为闰年来选择相应的天数,以下是示例代码
uint8_t days_in_month[2][13] = {
// 平年每个月的天数
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
// 闰年每个月的天数
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
// 判断是否为闰年
bool is_leap_year(uint16_t year) {
return ((year % 4 == 0 &
& year % 100 != 0) || (year % 400 == 0));
}
// 计算某年某月的天数
uint8_t days_in_month_for_year(uint16_t year, uint8_t month) {
return days_in_month[is_leap_year(year)][month];
}
想要计算出到某个月为止的天数,写一个循环就可以了
结尾
当你看了这里的代码之后,也许你会觉得这就是一个很简单很平常的东西,但就是这样一个看起来很简单的东西,如果平时缺少积累,是绝对想不到的,最后的结果就是你写出一段屎山