目录
10.1 keil5左上角的三个按钮Translate、build、rebuild
第13届蓝桥杯嵌入式第一场赛题:密码锁
1、结果:成绩省二
2、题目:密码锁
1、led 2、key 3、lcd 4、方波和PWM波切换输出PA1 5、uart
3、心得体会:
突击了一个月,有时候感觉自己能拿省一,有时候又感觉省三。最后拿了省二,也算是居中了哈哈。值得吐槽的是这次组委会出成绩太慢了,以往都是10天左右,这次花了3周,4.9号考完,五一放假前才出成绩。明年继续参加,目标要拿省一。
选择题很变态,以前都是单选和多选分开,现在是混在一起考。像我基础差的,选择题全靠蒙。但是大题就是中规中矩了,编程题考的都是基础模块,但是在考场把基础做好也不容易呀。考场突发情况太多了,比如一开始CubeMX不能生成keil5的模板,问题是Projcet Manager -> Advanced Settings -> Mcu and Firmware Package加载错了,和考场给的不匹配,要注意修改默认的Package,用考场给的资源包。
剩下的就没啥了,不紧张就好了,没必要一直看着5个小时的倒计时,容易心态乱奥。下面是我做的一套省赛模板,里面有按键Key、led、lcd、uart、i2c、adc、定时器tim(基本定时器、输入捕获、输出比较、PWM输出)、rtc时钟等等。大家想要什么资料可以评论奥。
//头文件
#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
#include "UART\bsp_uart.h"
#include "I2C\bsp_i2c.h"
#include "ADC\bsp_adc.h"
#include "TIM\bsp_tim.h"
#include "RTC\bsp_rtc.h"
//变量创建区
__IO uint32_t uwTick_Key_Set_Point = 0; //记录时钟滴答值(1ms变化一次),从而控制Key_Proc的执行速度。
__IO uint32_t uwTick_Led_Set_Point = 0;
__IO uint32_t uwTick_Lcd_Set_Point = 0;
__IO uint32_t uwTick_Uart_Set_Point = 0;
//*Key扫描专用变量
unsigned char ucKey_Val,unKey_Down,unKey_Up,ucKey_Old;
//*LED专用变量
unsigned char ucLed;
//*LCD显示专用变量
unsigned char Lcd_Disp_String[22]; //创建字符串数组
//*uart发送存储变量
uint8_t rx;
uint16_t counter;
uint8_t str[40];
//*I2C变量
uint8_t EEPROM_String_1[5] ={0x11,0x22,0x33,0x44,0x55};
uint8_t EEPROM_String_2[5] ={0};
//*RES4017可编程电阻
uint8_t RES_4017;
//*TIM6 测试
uint8_t i;
//*TIM2输入捕获 求频率和占空比
float PWM1_Duty; //占空比
uint16_t PWM1_D_Count; //高电平时间
uint16_t PWM1_T_Count; //全周期
//*RTC结构体定义
RTC_TimeTypeDef hour_min_sec_Time;
RTC_DateTypeDef year_mon_day_Date;
//*子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/*bsp资源的初始化*/
KEY_LED_Init();
LCD_Init();
//不可以把这三行放到void Lcd_Proc()函数里面去,会造成一直刷新闪烁。
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
//**串口初始化
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1,&rx,1); //串口接收打开中断
//**I2C初始化
I2CInit();
//**EEPROM测试读写
iic_24c02_write(EEPROM_String_1,0,5); //把EEPROM_String_1里的数据写入到EEPROM里,从地址0开始写
HAL_Delay(1); //读写之间加延时
iic_24c02_read(EEPROM_String_2,0,5); //读出数据,放到EEPROM_String_2里
//**可变电阻4017的测试读写
write_register(0x10); // 由公式20kΩ = (100kΩ * N)/127 + 10kΩ; 可以求出N=12.7,取整即为13 = 0x0D
HAL_Delay(1);
RES_4017 = read_register();
//**ADC初始化
MX_ADC1_Init();
MX_ADC2_Init();
//**TIM初始化
MX_TIM6_Init(); //基本定时器TIM6 一般设置1秒加1。
HAL_TIM_Base_Start_IT(&htim6);
//**输入捕获,接收555定时器和TIM3/17的PWM输入,可以显示频率和占空比。
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
//**输出比较,可以输出555方波,可以触发事件。而PWM输出不能触发,常用于控制电机等
MX_TIM15_Init();
HAL_TIM_OC_Start_IT(&htim15, TIM_CHANNEL_1);
HAL_TIM_OC_Start_IT(&htim15, TIM_CHANNEL_2);
//**输出PWM, 可以控制输出的频率和占空比。
MX_TIM3_Init();
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
//**输出PWM
MX_TIM17_Init();
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_2);
//**RTC初始化
MX_RTC_Init();
while (1)
{
Key_Proc();
Led_Proc();
Lcd_Proc();
Usart_Proc();
}
}
//***Key扫描子函数
void Key_Proc(void)
{
if((uwTick - uwTick_Key_Set_Point) < 50) return; //减速函数,延时50ms
uwTick_Key_Set_Point = uwTick;
//标准3行代码
ucKey_Val = Key_Scan(); //按键扫描Key_Scan只有1234四个值
unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val); //unKey_Down是检测某按键下降沿
unKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val); //ucKey_Val是检测按钮是否在按下状态
ucKey_Old = ucKey_Val;
if(unKey_Down == 4) //按键4按下,LD8和LD4亮起
{
ucLed = 0x88;
}
if(unKey_Down == 3) //按键3按下,全灭
{
ucLed = 0x00;
}
}
//***Led扫描子函数
void Led_Proc(void)
{
if((uwTick - uwTick_Led_Set_Point) < 200) return; //减速函数,延时200ms
uwTick_Led_Set_Point = uwTick;
LED_Disp(ucLed);
}
//**Lcd扫描子函数
void Lcd_Proc(void)
{
if((uwTick - uwTick_Lcd_Set_Point) < 200) return; //减速函数,延时200ms
uwTick_Lcd_Set_Point = uwTick;
//串口发送显示
sprintf((char *)Lcd_Disp_String,"Uart1_Transmit:%02d",counter);
LCD_DisplayStringLine(Line0, Lcd_Disp_String);
//*I2C显示
sprintf((char *)Lcd_Disp_String,"I2C_EEP:%x%x%x%x%x",EEPROM_String_2[0],EEPROM_String_2[1],EEPROM_String_2[2],EEPROM_String_2[3],EEPROM_String_2[4]);
LCD_DisplayStringLine(Line1, Lcd_Disp_String);
//*RES4017可编程电阻显示
sprintf((char *)Lcd_Disp_String,(const char *)"RES_K:%5.2fk", 0.7874*RES_4017);
LCD_DisplayStringLine(Line2, Lcd_Disp_String);
//*通过RES4017可编程电阻来求电压
sprintf((char *)Lcd_Disp_String,(const char *)"VOLTAGE:%5.2fV", 3.3*((0.7874*RES_4017)/((0.7874*RES_4017)+10))); //串联电阻求电压
LCD_DisplayStringLine(Line3, (uint8_t *)Lcd_Disp_String);
//**ADC1显示
sprintf((char *)Lcd_Disp_String,(const char*)"ADC1:%5.2fV",getADC1()/4096.0*3.3);
HAL_Delay(100); //注意要有延时
LCD_DisplayStringLine(Line4,(uint8_t *)Lcd_Disp_String);
//**ADC2显示
sprintf((char *)Lcd_Disp_String,(const char*)"ADC2:%5.2fV",getADC2()/4096.0*3.3);
HAL_Delay(100); //注意要有延时
LCD_DisplayStringLine(Line5,(uint8_t *)Lcd_Disp_String);
//**TIM6显示
sprintf((char *)Lcd_Disp_String,(const char*)"TIM6:%02dS",i);
LCD_DisplayStringLine(Line6,(uint8_t *)Lcd_Disp_String);
//**TIM2显示
sprintf((char *)Lcd_Disp_String,(const char*)"TIM2:%04dhz,%04.1f%%",(unsigned int)(1000000/PWM1_T_Count),PWM1_Duty*100);
LCD_DisplayStringLine(Line7,(uint8_t *)Lcd_Disp_String);
//RTC(时间和日期必须同时获取,不要在中间分开)
HAL_RTC_GetTime(&hrtc, &hour_min_sec_Time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &year_mon_day_Date, RTC_FORMAT_BIN);
sprintf((char *)Lcd_Disp_String,(const char*)"RTC_Time:%02d:%02d:%02d",(unsigned int)hour_min_sec_Time.Hours,(unsigned int)hour_min_sec_Time.Minutes,(unsigned int)hour_min_sec_Time.Seconds);
LCD_DisplayStringLine(Line8,(uint8_t *)Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String,(const char*)"RTC_Date:%02d-%02d-%02d",(unsigned int)year_mon_day_Date.Year,(unsigned int)year_mon_day_Date.Month,(unsigned int)year_mon_day_Date.WeekDay);
LCD_DisplayStringLine(Line9,(uint8_t *)Lcd_Disp_String);
}
void Usart_Proc(void)
{
if((uwTick - uwTick_Uart_Set_Point) < 1000) return; //减速函数,延时1s
uwTick_Uart_Set_Point = uwTick;
//串口显示
sprintf((char *)str,"Uart1_Transmit:%02d\r\n",counter);
HAL_UART_Transmit(&huart1,(uint8_t *)str,strlen(str),50); //50ms是发送超时时间。如果波特率为9600,发送一个位需要的时间为1/9600s=0.0001042s=0.1042ms,计算...
if(++counter == 60)
counter = 0;
}
//**串口接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
LED_Disp(0xc0);
HAL_Delay(300); //在串口中断里嵌套了滴答systick定时器中断,因此systick中断优先级应该更高。
LED_Disp(0xff);
HAL_UART_Receive_IT(&huart1,&rx,1); //此行必须保留
}
//**TIM6 基本定时器 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
i++;
HAL_TIM_Base_Start_IT(&htim6);
}
}
//**TIM2 输入捕获 中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) //高电平通道
{
PWM1_T_Count = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1)+1; //整个周期 T
PWM1_Duty = (float)PWM1_D_Count/PWM1_T_Count;
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
PWM1_D_Count = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2)+1; //高电平持续时间D
}
}
}
**TIM15 输出比较 中断回调函数
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM15)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, __HAL_TIM_GetCounter(htim)+100); //5khz
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, __HAL_TIM_GetCounter(htim)+500); //1khz
}
}
}
1、K.e.i.l.5 2032版破解软件
为了大家方便,分享一下2032的软件。
注意:要以管理员身份打开 ,否则报错奥!
链接:https://pan.baidu.com/s/1QrtW42YD5xp-y-PY6p50hw
提取码:Lu26
--来自百.度..网.盘.超.级.会.员.V3的分享
2、背景颜色修改问题
最终效果:
具体步骤:点击"Edit"-> "Configuration" 到如下界面。
一定要把Text,Number,....直到Incomplete String等等10多个类型的Background都给改了。否则就会出现文末的常见问题。
为了方便,可以填一个自定义颜色。不用一直输入红绿蓝的颜色数值了。
知网 CAJViewer 的背景色
具体步骤:点击工具"-> "参数设置" 到如下界面。
3、GPIO的输入输出
详见蚂蚁工程的PPT->第05章 LED指示灯程序设计
4、NVIC中断的理解和流程
听了蚂蚁的课,对于STM32中断的理解。
1、前期准备工作
①初始化引脚为中断模式。 //在CubeMX中配置
②HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2)
//设置中断优先级分组,在文件stm32xxx_hal_msp.c中。
③HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 1);
//配置中断优先级,一般在对应的.c文件中
④HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能外中断,同上。
2、断信号产生(按键KEY4按下),进入IRQHandler函数。
3、在中断函数IRQHandler中再次判断是否产生中断标志if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u),如果确实产生了中断标志,则执行中断服务函数。即进入Callback回调函数。
4、执行回调函数
5、IIC相关操作
1、要把bsp_iic.c文件中的代码行SDA_Output_Mode(); 换一下顺序。从1、换成2、
1、 2、
SDA_Output_Mode(); SCL_Output(0);
SCL_Output(0); delay1(DELAY_TIME);
delay1(DELAY_TIME); SDA_Output_Mode();
2、手写AT24C02的写程序
6、ADC采集电压显示
6.1 基本参数记录
1、引脚和通道
ADC1 引脚PB12 通道IN11 电阻R38
ADC2 引脚PB15 通道IN15 电阻R37
2、时钟分频选择异步2分频,不需要配置中断。
3、注意时钟的变化:
6.2问题及注意事项
1:LCD会显示电压恒为 0V ?
sprintf(buf, "R38_VAL:%6.2fV", getADC()/4096*3.3);
原因:
getADC( ) / 4096,因为getADC()比较小对4096取余恒为0,乘以3.3后还是0。
解决方法:
核心思想是把整型运算变成浮点数运算。
1、(float)getADC()/4096*3.3) //强制转换结果为浮点数
2、getADC()/4096.0 * 3.3) // 给4096变成4096.0,变成浮点数运算
3、getADC()*3.3/4096 //先乘以3.3,变成浮点数,再除4096
注:
① %6.2f的格式含义是,数字整体长度包括小数点为6位,保留两位小数,位数大于6则输出实际位数。
② %5d即宽度至少为5位,右对齐,位数大于5则输出实际位数。
%0nd 用得比较多,表示输出的整型宽度至少为n位,不足n位用0填充。
printf("%05d",1)输出:00001
printf("%5d",1)输出:****1(*为空格)
③常用转义字符
常用转义字符 | |
\b | 退格符(Backspace) |
\t | 水平制表符(相当于tab,缩进) |
\n | 换行符 |
\f | 换页符 |
\r | 回车符 |
\r\n | Enter = 回车+换行( \r\n ) |
\\ | 转义输出\ |
%% | 转义输出% |
7、定时器TIM相关
定时器序号 | 功能 | 启动函数区别 | 回调函数 | 注意事项 |
TIM6 | 单纯计时功能,无对应的引脚。 | HAL_TIM_Base_Start_IT(); | HAL_TIM_PeriodElapsedCallback (); 配置值:分频为1Hz。7999、9999 | 需要配置中断 |
TIM2 | 输入捕获来显示频率和占空比 | HAL_TIM_IC_Start_IT (); | HAL_TIM_IC_CaptureCallback (); 配置值:分频为15Hz左右。79、65535 | 需要配置中断 |
TIM15 | 输出比较模式(方波等) | HAL_TIM_OC_Start_IT (); | HAL_TIM_OC_DelayElapsedCallback(); 配置值:同上 | 需要配置中断 |
TIM3/17 | PWM输出模式,不同频率和占空比 | HAL_TIM_PWM_Start (); | 配置值:分频为1000Hz。79、999 | |
Base代表基本定时器Tim6;IC(Input Capture)代表输入捕获Tim2; OC(Output Compare)代表输出比较Tim15; |
7.1 基本定时器TIM6
功能:单纯计时功能。无对应的引脚。
TIM6配置参数:(需要中断)
7.2 通用定时器TIM2(需要中断)
1、功能:输入捕获来显示频率和占空比,例如555方波和TIM3/17输出的PWM信号。
2、PWM输入捕获占空比的原理,如下图。
3、TIM2配置参数:
注:TIM2的通道CH1,在GPIO相关配置中更改引脚为PA15。
当Prescaler为79时,把时钟80Mhz分频为1Mhz;Counter Period选择最大65535。
7.3 通用定时器TIM15
1、功能:比较输出模式,可以触发事件。而PWM输出不能触发,常用于控制电机等。
2、引脚TIM15 -> PA2。
3、TIM12配置参数
7.4 通用定时器TIM3/17(不需要中断)
1、功能:PWM输出,可以输出不同频率和占空比的PWM信号。
2、引脚:TIM3 -> PA6 ;TIM17 -> PA7 ;
3、TIM3配置参数:
4、TIM17配置参数:
4、TIM3(PA6)和TIM17(PA7)引脚连线图:
8、RTC时钟
配置参数如下:
注意: 别忘了时钟切换奥!
9、常用函数的寻找
9.1 systick滴答定时器优先级设置
举例:在uart回调函数里 调用HAL_Delay(),就要使滴答定时器uwTick的优先级高于串口。
修改答定时器uwTick优先级的步骤:
第一步:
第二步:
第三步:
9.2 新建工程要添加的配置(蓝桥杯嵌入式比赛)
还要注意adc 要添加3个底层文件,其中特殊的是stm32xx_II_adc.c文件
10、keil5 MDK的使用技巧
10.1 keil5左上角的三个按钮Translate、build、rebuild
第1个按钮(Translate):
作用:检查某个文件是否有语法错误。
速度:快
第2个按钮(build):
作用:编译器会记录上次编译过的源程序,在下次编译的过程中只对修改过的编译。
速度:较快
优点:大大节省编译时间,建议常用。
第3个按钮(rebuild):
作用:检查某个文件是否有语法错误。
速度:很慢
注:如果修改了优先级等全局变量,最好用这个按钮全编译一下。
11、UART的配置
注意引脚改为PA10 PA9。