因为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
使能定时中断
c
//定时中断需要使能
HAL_TIM_Base_Start_IT(&htim2); //HAL_TIM_Base_Start_IT函数用于以中断模式启动定时器
长按键、短按键(使用TIM3)
利用cnt计时
本质就是将计算 T 公式里的: ARR 代替为 CNT(计数值)------ T = 1 / f = CNT * (PSC + 1) / f
LCD高亮显示
c++
//按键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){ //按下B4
temp_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通信(异步、全双工、字节传输)
配置

类型转换问题
c++
字符类型(char)在算术运算中会被转换为整数类型(int)。
short型会被转换为int型。
float型会被转换为double型以提高精度。
当有符号数与无符号数进行运算时,所有操作数自动转换为无符号类型
字符与字节
存储字符串 "bens in 2023/23/23" 的 char 数组需占用 19 字节,空格也算字符,包含'\0'作为终止符(转义字符\n,\t也算字符)
代码实现(向上位机发送数据)
c++
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); //超时时间限制为50ms
HAL_Delay(2000); //发送数据间隔时间: 0.1s
}
代码实现(按字节循环接收uart串口数据)
主函数
c++
//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配置

代码实现