因为lED和LCD共用PC8~PC15引脚,要通过锁存(LE)和(GPIOC->ODR)来避免LED和LCD引脚冲突
修改点:
- main.c中,GPIO初始化引脚后,LE(PD2引脚低电平锁存,退出透明模式,LCD的输出就无法影响LED引脚的状态)
- LCD_DisplayStringLine();
- LCD_Clear()
定时器中断
定时器功能
定时器频率和周期公式
- PSC(预分频系数): 1 / f == T, 乘上(PSC+1) 得到执行一次累加操作的实际周期,再乘上(ARR+1)等于中断周期
- 预分频理解: 假设原本1ms执行一次累加, PSC == 2, 则当前变成2ms才执行一次累加(另外的1ms分给了别的任务)
- 时钟配置
- 通过STM32CubeMX,配置TIM2(通用定时器)的参数(Fsystem = 80MHz = 80 000 000)
假设定时中断周期要求 1s , 则 ARR+1=8000 , PSC+1 = 10000
使能定时中断
//定时中断需要使能HAL_TIM_Base_Start_IT(&htim2); //HAL_TIM_Base_Start_IT函数用于以中断模式启动定时器
长按键、短按键(使用TIM3)
利用cnt计时
本质就是将计算 T 公式里的: ARR 代替为 CNT(计数值)—— T = 1 / f = CNT * (PSC + 1) / f
LCD高亮显示
//按键4通过循环取余数,控制lcd某行高亮显示
static uint16_t temp_line = 0;
void Hight_light_lcd_line(){key_s4 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);if(key_s4==RESET && k4_last_state==SET){ //按下B4temp_line++;temp_line%=3;HAL_Delay(200);}//控制功能栏轮流高亮显示if(temp_line==0){//sprintf(); 用于写入字符串到字符数组LCD_SetBackColor(Red); //该行以下的背景颜色是红色sprintf(text, " Hello, Vodka! ");LCD_DisplayStringLine(Line3, (uint8_t*) text );LCD_SetBackColor(Black); //该行以下的背景颜色是黑色sprintf(text, " Count: %d ", Count);LCD_DisplayStringLine(Line4, (uint8_t*) text);sprintf(text, " LightMode: %d ", lightMode);LCD_DisplayStringLine(Line5, (uint8_t*) text);}else if(temp_line==1){//sprintf(); 用于写入字符串到字符数组sprintf(text, " Hello, Vodka! ");LCD_DisplayStringLine(Line3, (uint8_t*) text );LCD_SetBackColor(Red); sprintf(text, " Count: %d ", Count);LCD_DisplayStringLine(Line4, (uint8_t*) text);LCD_SetBackColor(Black); sprintf(text, " LightMode: %d ", lightMode);LCD_DisplayStringLine(Line5, (uint8_t*) text); }else if(temp_line==2){//sprintf(); 用于写入字符串到字符数组sprintf(text, " Hello, Vodka! ");LCD_DisplayStringLine(Line3, (uint8_t*) text );sprintf(text, " Count: %d ", Count);LCD_DisplayStringLine(Line4, (uint8_t*) text);LCD_SetBackColor(Red); sprintf(text, " LightMode: %d ", lightMode);LCD_DisplayStringLine(Line5, (uint8_t*) text);LCD_SetBackColor(Black); }//LED闪烁,间隔两秒闪烁一次light_led(3,lightMode%2);}
PWM波形
原理
占空比:高电平持续时间占整个周期时长的比值
PWM频率和占空比的概念:占空比由捕获比较寄存器(CCR)决定,而频率由ARR和PSC共同决定。不同的PSC和ARR组合可以得到相同的频率,只要它们的乘积相同。例如,PSC=1和ARR=1000,与PSC=2和ARR=500,得到的频率是一样的,但占空比的调节范围可能不同,因为ARR的值不同。
CubeMX配置
初始化
CCR(输出比较寄存器)
输入捕获测量引脚输出PWM波形
通过计算两个中断之间CNT差值(capture_value),得出中断周期(捕获周期)
捕获周期计算公式
信号发生器
PWM输出引脚和输入捕获引脚,板子上的引脚R37~R40分别是:电压采集1、电压采集2、频率输出1(PB4)、频率输出2(PA15)
例题
TIM17配置(记得配置NVIC)
pwm输入捕获代码
PA1、PA7需要杜邦线连接; PA1配置TIM2的通道2、PA7配置TIM17的通道1
输入捕获555定时器的频率
时钟通道配置
引脚PA15和引脚PB4测量频率输入1和频率输入2
代码
TIM2、TIM16输入捕获中断开启
TIM2、TIM16输入捕获展示
ADC测量
配置ADC1和ADC2
代码实现
USART通信(异步、全双工、字节传输)
配置
类型转换问题
字符类型(char)在算术运算中会被转换为整数类型(int)。
short型会被转换为int型。
float型会被转换为double型以提高精度。
当有符号数与无符号数进行运算时,所有操作数自动转换为无符号类型
字符与字节
存储字符串 "bens in 2023/23/23" 的 char 数组需占用 19 字节,空格也算字符,包含'\0'作为终止符(转义字符\n,\t也算字符)
代码实现(向上位机发送数据)
void UART_Tx_Test(void){sprintf((char*)uart_tx_text,"Hey~ Vodka~\r\n"); HAL_UART_Transmit(&huart1, (uint8_t *)uart_tx_text, strlen(uart_tx_text), 50); //超时时间限制为50msHAL_Delay(2000); //发送数据间隔时间: 0.1s }
代码实现(按字节循环接收uart串口数据)
主函数
//util.h
#ifndef __util_h
#define __util_h
//串口接收变量
extern char uart_rx_text[22];
extern uint8_t Rx_Byte;
extern uint8_t Rx_Index;
extern uint8_t Rx_DoneFlag;//车辆信息(串口接收)
extern char car_type[5];
extern char car_data[5];
extern char car_time[13];//测试函数
void lcd_test(void); //ADC测量
void ADC_show(void); //ADC测量电压
void ADC_Caculate_Volt(void);//串口发送测试
void UART_Tx_Test(void);//串口接收测试(只能通过中断接收)
void UART_Rx_Test(void);//UART中断数据后续处理(放在主程序里循环)
void uart_data_process(void);#endif//util.c
//UART中断回调接收数据(第一次中断在主函数)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef * huart){if(huart->Instance == USART1){uart_rx_text[Rx_Index++] = Rx_Byte;//判断接收的字节是否完成if(Rx_Index==22||Rx_Byte=='\n'){Rx_DoneFlag = 1;}//继续开启下一次中断HAL_UART_Receive_IT(&huart1,&Rx_Byte,sizeof(Rx_Byte));}
}//UART中断数据后续处理(放在主程序里循环)
void uart_data_process(void){//判断中断接收完毕标志if(Rx_DoneFlag==1){if(Rx_Index <= 22){ //接收数组长度22字节,除去\0,实际只能存储21个字节sscanf(uart_rx_text, "%4s %4s %10s", car_type, car_data, car_time);//展示数据LCD_SetBackColor(Yellow);LCD_SetTextColor(Black); sprintf(rx_display_text," UART_Rx ");LCD_DisplayStringLine(Line0,rx_display_text); sprintf(rx_display_text," car_type:%s ",car_type);LCD_DisplayStringLine(Line1,rx_display_text);sprintf(rx_display_text," car_data:%s ",car_data);LCD_DisplayStringLine(Line2,rx_display_text);sprintf(rx_display_text," car_time:%s ",car_time);LCD_DisplayStringLine(Line3,rx_display_text); }else{sprintf(rx_display_text," Rx_Error\r\n ");HAL_UART_Transmit(&huart1, (uint8_t*)rx_display_text, strlen(rx_display_text), 50); }//重置相应标志Rx_DoneFlag = 0;Rx_Index = 0;memset(uart_rx_text,0,strlen(uart_rx_text));}
}
通过定时器,UART串口接收不定长的数据
原理
传送一个字节花费的时间,转化为CNT的值来判断
间隔指定时间读取一个数据
判断结束的标志:最后一个位的时间大于1.5ms
配置
TIM4:内部时钟、波特率:9600、 开启中断、 PSC: 8000-1
串口中断开启、时钟使能
中断处理、中断回调
自行编写
IIC
IIC原理图
非易失性存储器
从机地址
读写位:1是读、2是写
代码实现
RTC实时时钟
设置(读取)时间、日期,设置闹钟
RTC配置
代码实现