1 LED
点亮LED的步骤
1 PD2 输出高电平
2 然后再将PC8-13置为低电平(低电平有效)
3 PD2 输出低电平
如果不想让LED上电就亮,在初始化之前给PD2低电平就可以了
所有的代码都要写在BEGIN END之间,要不然修改CubMX的时候,代码都会消失
通过一个headfile.h文件所有的头文件包含都放在这个里面管理
由地址编码可以知道GPIO_PIN_8左移一位的话,就得到了GPIO_PIN_9


2 按键

按键扫描放到while循环中就可以了
同时,按键要支持长摁和段摁(需要一个定时器去配合)
思路:储存两个状态当摁下时候,state 变为0 如果last_state为1就说明刚开始按下,那么就要把定时器清零,如果state ==0 last_state ==0 说明是长摁(长摁需要设定时间,一般一秒或者两秒就可以)
当state == 1 last_state ==0 的时候,说明是按键松开了,这时候就要去判断定时器,如果小于1s(设定的值)那么就去执行相应的操作

组合按键
3 lcd
解决lcd与按键引脚冲突的问题
吧所有用到lcd的函数都这样写一下
Init 和clear 就要写一下,下面两个就不用了

lcd的高亮显示(能够去设置界面,有选中标志)
cs
char text[20];
void lcd_display()
{
sprintf(text," Test ");
LCD_DisplayStringLine(Line0,(uint8_t *)text);
sprintf(text,"count:%d ",count);
LCD_DisplayStringLine(Line1,(uint8_t *)text);
if(hight_light==0)
{
LCD_SetBackColor(Yellow);
sprintf(text," parm1 ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
LCD_SetBackColor(Black);
sprintf(text," parm2 ");
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text," parm3 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
}
else if(hight_light==1)
{
sprintf(text," parm1 ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
LCD_SetBackColor(Yellow);
sprintf(text," parm2 ");
LCD_DisplayStringLine(Line3,(uint8_t *)text);
LCD_SetBackColor(Black);
sprintf(text," parm3 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
}
else if(hight_light==2)
{
sprintf(text," parm1 ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," parm2 ");
LCD_DisplayStringLine(Line3,(uint8_t *)text);
LCD_SetBackColor(Yellow);
sprintf(text," parm3 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
LCD_SetBackColor(Black);
}
led_show(1,led_mode);
}
4 定时器
使用定时器的时候,一定要去调用函数使能定时器,定时中断和PWM输出也是需要去使能的

pwm输出


输入捕获获得PWM周期T

这个capture_value 是通过捕获引脚检测到PWM的一个上升沿的时候发生中断开始计时,下一个中断到来的时候,两次中断的时间差就是capture_value
系统的t0=1/(系统频率/(PSC+1))
输入捕获是定时器的一种模式

我们捕获的时候,一般是要得到CNT 的值,在函数里面可知,将CCR的值返回了,这是因为在捕获到上升沿的时候,会将CNT赋值给CCR
所以可以不用去调用函数,直接获取CCR的值就可以了
cpp
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM17)
{
capture_val = TIM17->CCR1;
TIM17->CNT = 0;
freq1 = 80000000/(80*(capture_val+1));
}
if(htim->Instance == TIM3)
{
capture_val = TIM3->CCR1;
TIM3->CNT = 0;
freq2 = 80000000/(80*(capture_val+1));
}
if(htim->Instance == TIM8)
{
capture_val = TIM8->CCR1;
TIM8->CNT = 0;
freq3 = 80000000/(80*(capture_val+1));
}
}
5 adc测量
cpp
double get_adcvalue(ADC_HandleTypeDef * hadc)
{
HAL_ADC_Start(hadc);
uint32_t adc_value = HAL_ADC_GetValue(hadc);
return 3.3*adc_value/4096;
}
adc使用的时候还需要校准!!!!
6 串口
发送的时候加上\r\n就是换行的作用
利用定时器进行串口不定长数据接收

就是用一个计数器,在每一次接收到数据的时候,将计数器的CNT清零,在另外一个函数中判断,如果计数器>15的时候,就说明接收一次完成(这个函数放到whie循环中即可)
这样的处理比较符合赛题,如果自己写的话,不定长数据接收是要用帧头和帧尾来判断的
cpp
uint8_t rxdata;
uint16_t count;
uint8_t rx_flag;
uint8_t rx_buff[20];
char send_buff[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
//HAL_UART_Transmit(&huart1,&rxdata,1,50);
TIM4->CNT = 0;
rx_flag = 1;
rx_buff[count] = rxdata;
count++;
HAL_UART_Receive_IT(&huart1,&rxdata,1);
}
}
void uart_data_rec()
{
if(rx_flag)
{
if(TIM4->CNT>15)
{
if(rx_buff[0]=='l' && rx_buff[1]=='a' && rx_buff[2]=='n')
{
sprintf(send_buff,"lan\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
}
else if(rx_buff[0]=='q' && rx_buff[1]=='i' && rx_buff[2]=='a' && rx_buff[3]=='o')
{
sprintf(send_buff,"qiao\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
}
else if(rx_buff[0]=='b' && rx_buff[1]=='e' && rx_buff[2]=='i')
{
sprintf(send_buff,"bei\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
}
else
{
sprintf(send_buff,"Error!\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
}
rx_flag = 0;
for(int i = 0;i<count;i++)
{
rx_buff[i]=0;
}
count = 0;
}
}
}
以上的代码基本上比赛时不能用,因为这个稳定性不高,如果while循环中还有打印,按键检测的话,这个串口接收的数据就会出问题,建议使用DMA串口接收发送
其实就是用DMA串口接收+printf重映射发送,因为DMA能够自动数出不定长数据的长度
找一下sscanf() 函数的使用方法(可以解析固定格式的一个字符串数据)
sscanf函数
十二届省赛真题就可以这样用
strtok函数

atoi函数

7eeprom:非易失性存储器
很简单,就是包含iic文件之后用人家的,写两个函数就可以了
cpp
void eeprom_write(uint8_t addr,uint8_t data)
{
I2CStart();
I2CSendByte(0xa0);//AT24c02的地址
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
HAL_Delay(20);
}
uint8_t eeprom_read(uint8_t addr)
{
I2CStart();
I2CSendByte(0xa0);//AT24c02的地址
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);//读取
I2CWaitAck();
uint8_t data = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return data;
}
输入0xa0是写入,输入0xa1是读取,注意这个读取的时候的延时20ms是必不可少的
8 rtc实时时钟
配置界面
起始就是在MX上配置,打开中断(作为闹钟)
cpp
uint8_t rtc_flag;
char text_time[20];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
void rtc_lcd_show()
{
HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
sprintf(text_time," text ");
LCD_DisplayStringLine(Line0,(uint8_t *)text_time);
sprintf(text_time," %2d-%2d-%2d ",sTime.Hours,sTime.Minutes,sTime.Seconds);
LCD_DisplayStringLine(Line1,(uint8_t *)text_time);
sprintf(text_time," %d-%d-%d-%d ",sDate.Year,sDate.Month,sDate.Date,sDate.WeekDay);
LCD_DisplayStringLine(Line2,(uint8_t *)text_time);
led_show(1,rtc_flag);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
rtc_flag = 1;
}
9滴答定时器
cpp
uint32_t my_tick;
void main_proc()
{
if(uwTick-my_tick>=100)
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_11);
my_tick = uwTick;
}
}
这个函数的功能就是每1s反转一次LED4, 采取了滴答定时器的方法(不能用中断的方法,因为程序中如果用了lcd的话,中断打断lcd再去操作led会起冲突,用这样的方法就不会起冲突)
还有一种法方
通过it.c文件中的一个函数(这个函数中断1ms进入一次,所以就可以把自己代码中的东西写上去也可以)

10 屏幕反转代码
就是在lcd.c里面写上这个就可以了
cpp
// 全局标志位:0表示正常显示,1表示翻转显示
uint8_t display_flip_flag = 0;
/**
* @brief LCD屏幕显示模式切换
* @param flip: 0=正常, 1=翻转(180度)
*/
void LCD_DisplayMode_Switch(uint8_t flip)
{
if(flip)
{
// 配置为翻转模式 (垂直+水平翻转)
LCD_WriteReg(R1, 0x0100); // 设置SS位,垂直翻转(自下而上)
LCD_WriteReg(R96, 0xA700); // 设置GS位,水平翻转(自右而左)
}
else
{
LCD_WriteReg(R1, 0x0000); // 官方默认
LCD_WriteReg(R3, 0x1018); // 官方默认!必须写这个值
LCD_WriteReg(R96, 0x2700); // 官方默认!必须写这个值
}
display_flip_flag = flip;
// 翻转后建议清屏重绘,防止残留杂点
LCD_Clear(Black);
}
比赛要用模板更快

