蓝桥杯嵌入式的总结
一 .LED灯
1 .cubeMX配置
把pb8~pb15配置成output, pd2也配置成output, 把pb8~pb15的输出引脚配置成高电平
pd2默认
在keil中创建一个bsp文件,方便提交
2 .keil中代码
.c文件
#include "bsp_led.h"
void Disp_Led(uint16_t led_num)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,led_num<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
.h文件
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "main.h"
void Disp_Led(uint16_t led_num);
#endif
二 .LCD屏(移植)
找到官方给的lcd例程,将其复制到自己的工程文件夹中
以下两部分一般放在while之前。
lcd初始化:
LCD_Init(); //初始化
LCD_Clear(Black); //清屏
LCD_SetBackColor(Black); //背景色
LCD_SetTextColor(White); //前景色
显示:
char text[30];
sprintf (text," ni hao ya " );
LCD_DisplayStringLine(Line0,(uint8_t *)text);
LCD_DisplayStringLine(Line1, (unsigned char *)" ");
LCD_DisplayStringLine(Line2, (unsigned char *)" ");
LCD_DisplayStringLine(Line3, (unsigned char *)" ");
LCD_DisplayStringLine(Line4, (unsigned char *)" ");
LCD_DisplayStringLine(Line5, (unsigned char *)" ");
LCD_DisplayStringLine(Line6, (unsigned char *)" ");
LCD_DisplayStringLine(Line7, (unsigned char *)" ");
LCD_DisplayStringLine(Line8, (unsigned char *)" ");
LCD_DisplayStringLine(Line9, (unsigned char *)" ");
三 .按键
1 .一般按键
配置成上拉模式
开启tim1(根据实际情况而定)的定时器和中断,配置成10ms方便消抖
key.c文件
#include "key.h"
#include "stdio.h"
struct keys key[4]={0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance==TIM1)
{
key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(char i=0;i<4;i++)
{
switch (key[i].jude_sta)
{
case 0:
{
if(key[i].key_sta==0)
{
key[i].jude_sta=1;
}
}
break;
case 1:
{
if(key[i].key_sta==0)
{
key[i].jude_sta=2;
key[i].key_flog=1;
}
else
{
key[i].jude_sta=0;
}
}
break;
case 2:
{
if(key[i].key_sta==1)
{
key[i].jude_sta=0;
}
}
break;
}
}
}
}
key.h文件
#ifndef __KEY_H
#define __KEY_H
#include "main.h"
#include "stdbool.h"
struct keys
{
bool key_sta;
char jude_sta;
bool key_flog;
};
#endif /*__KEY_H*/
2.长按键(按压时间>70ms)
.c文件
#include "key_chang.h"
struct key_chang keys_chang[4]={0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
keys_chang[0].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
keys_chang[1].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
keys_chang[2].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
keys_chang[3].key_chang_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(char i=0;i<4;i++)
{
switch (keys_chang[i].jude_sta)
{
case 0:
{
if(keys_chang[i].key_chang_sta==0)
{
keys_chang[i].jude_sta=1;
keys_chang[i].key_chang_time=0;
}
}
break;
case 1:
{
if(keys_chang[i].key_chang_sta==0)
{
keys_chang[i].jude_sta=2;
}
else
{
keys_chang[i].jude_sta=0;
}
}
break;
case 2:
{
if(keys_chang[i].key_chang_sta==1)
{
keys_chang[i].jude_sta=0;
if(keys_chang[i].key_chang_time<70)
{
keys_chang[i].key_flog=1;
}
}
else
{
keys_chang[i].key_chang_time++;
if(keys_chang[i].key_chang_time>70)
{
keys_chang[i].key_chang_flog=1;
}
}
}
break;
}
}
}
}
.h文件
#ifndef __KEY_CHANG_H
#define __KEY_CHANG_H
#include "main.h"
#include "stdbool.h"
struct key_chang
{
char jude_sta;
bool key_chang_sta;
bool key_chang_flog;
uint16_t key_chang_time;
bool key_flog;
};
#endif /*__KEY_CHANG_H*/
3 .双击按键(针对短按键)
.c文件
#include "key_shuang.h"
struct key_chang keys_chang[4]={0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM1)
{
keys_chang[0].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
keys_chang[1].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
keys_chang[2].key_chang_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
keys_chang[3].key_chang_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(char i=0;i<4;i++)
{
switch (keys_chang[i].jude_sta)
{
case 0:
{
if(keys_chang[i].key_chang_sta==0)
{
keys_chang[i].jude_sta=1;
keys_chang[i].key_chang_time=0;
}
}
break;
case 1:
{
if(keys_chang[i].key_chang_sta==0)
{
keys_chang[i].jude_sta=2;
}
else
{
keys_chang[i].jude_sta=0;
}
}
break;
case 2:
{
if((keys_chang[i].key_chang_sta==1)&&(keys_chang[i].key_chang_time<70))
{
if(keys_chang[i].key_shuangclick_num==0)
{
keys_chang[i].key_shuangclick_num=1;
keys_chang[i].key_shuangclick_time=0;
}
else
{
keys_chang[i].key_shuangclick_flog=1;
keys_chang[i].key_shuangclick_num=0;
}
keys_chang[i].jude_sta=0;
}
else if((keys_chang[i].key_chang_sta==1)&&(keys_chang[i].key_chang_time>=70))
{
keys_chang[i].jude_sta=0;
}
else
{
if((keys_chang[i].key_chang_time>=70))
{
keys_chang[i].key_chang_flog=1;
}
keys_chang[i].key_chang_time++;
}
}
break;
}
if(keys_chang[i].key_shuangclick_num==1)
{
keys_chang[i].key_shuangclick_time++;
if(keys_chang[i].key_shuangclick_time>35)
{
keys_chang[i].key_flog=1;
keys_chang[i].key_shuangclick_num=0;
}
}
}
}
}
.h文件
#ifndef __KEY_SHUANG_H
#define __KEY_SHUANG_H
#include "main.h"
#include "stdbool.h"
struct key_chang
{
char jude_sta;
bool key_chang_sta;
bool key_chang_flog;
uint16_t key_chang_time;
bool key_flog;
uint16_t key_shuangclick_num;
uint16_t key_shuangclick_time;
bool key_shuangclick_flog;
};
#endif
注意:结构体在main.c中调用时要extern声明,且还要打开定时器中断:
HAL_TIM_Base_Start_IT(&htim1)
四.pwm波的输出
1.cubeMX配置
其中用到的定时器,要根据实际情况选择,
周期T=(psc+1)*ARR/内部时钟,其中的占空比由ARR来决定的,一般ARR设置为100,方便占空比的计算。
要开HAL_TIM_PWM_START(&htimx,TIM_CHANNEL_X)
若要改变占空比,__HAL_TIM_SetCompara(&htimx,TIM_CHANNEL_X,占空比值),对其占空比进行设置
具体情况要根据题目来.
五 .输入捕获
1 .原理
是对要捕获的波进行频率,占空比的测量,实质时,对要捕获的波进行掐时 ,在上升沿处掐时,可以算频率,在下降沿掐时可以算占空比,一个注意的地方:在配置输入捕获时要配置PSC与ARR(相当于定时器)其一个周期一定要大于要捕获的波的周期(一般大两倍),放置捕获不完全,换句话说,如果小了,当波的下一个上升沿还未到来时,我的计数时间已经从新从0开始了,必然造成错误
2 .cubeMX配置
3 .代码
将其放在按键的.c文件中
/********************输入捕获************************/
double frequency;
uint16_t duty_cycle;
double frequency_jishuan;
uint16_t duty_cycle_jishuan;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //输入捕获回调函数
{
if(htim->Instance==TIM3)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)//通道检测
{
frequency=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1); //获取通道一的直接测量值,用于计算频率
duty_cycle=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);//获取通道二的直间接量值,用于计算占空比
__HAL_TIM_SetCounter(htim,0);//清零计数值
frequency_jishuan=(80000000/80)/frequency;//计算频率
duty_cycle_jishuan=(duty_cycle/frequency)*100;//计算占空比,因为占空比就等于上升沿的计数值比上整个周期的值再乘以100
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);//开启通道一
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);//开启通道二
}
}
}
主函数初始化两个通道:
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);//计算频率的通道
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);//计算高电平时间的通道
六 .ADC
1 .cubeMX的配置
配置比较简单
2 .代码
.c文件
#include "bsp_adc.h "
float getADC(ADC_HandleTypeDef *pin)
{
uint16_t adc;
HAL_ADC_Start(pin); //开启adc通道
adc = HAL_ADC_GetValue(pin); // 获取adc读出返回的值
return adc*3.3/4096; //转换成0~3.3V的电压,因为我们的ADC2是12bit,也就是2的12次方,所以要除以4096
}
.h文件
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "main.h"
float getADC(ADC_HandleTypeDef *pin);
#endif
七 .IIC读写EEPROM
1 . cubeMX配置
把PB6、PB7的GPIO配置成输出模式即可
然后导入官方资料包给的IIC文件
2 .代码
在导入的.c文件后面加入下面代码,记得.h文件中声明
void eeprom_write(unsigned char adder,unsigned char dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(adder);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char eeprom_read(unsigned char adder)
{
unsigned char datt;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(adder);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
datt = I2CReceiveByte();
I2CWaitAck();
I2CStop();
return datt;
}
我们的EEPROM一个数据存放单元只能存放一个字节的数据
如果需要存入一个int(两个字节)类型的数据,需要分开存放进行写入,注意写入需要一定的时间,因此需要进行一个10ms的延时
读的时候需要进行位操作,把第一个字节的数据移到高八位,再与上我们的低八位即可
写:
read_count1 = eeprom_count >> 8;
read_count2 = eeprom_count&0xff;
eeprom_write(1,read_count1);
HAL_Delay(10);
eeprom_write(2,read_count2);
读:
eeprom_count = (eeprom_read(1)<<8) + eeprom_read(2);
八 .串口通信
1.cube
配置波特率以及开启中断.
2.代码
串口的接收:
char data[30];
uint8_t rx;
uchar rx_flag;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
data[rx_flag++] = rx;
HAL_UART_Receive_IT(&huart1,&rx,1);
}
//还可以这样写
uint8_t Rxbuff[30];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
// 重新使能中断
HAL_UART_Receive_IT(huart,(uint8_t *)&Rxbuff,sizeof(Rxbuff));
}
}
主函数声明一下:HAL_UART_Receive_IT(&huart1,&rx,1);
接收例子:
void uart_porc(void)
{
sscanf(data,"%s",uart_rx); //把接受的字符串写到uart_rx字符串数组里面
if(rx_flag>0) //如果接收到字符
{
if(rx_flag == 3) //判断接受的字符串长度为3
{
char temp[20];
sprintf(temp,"%s",uart_rx);
LCD_DisplayStringLine(Line3,(unsigned char *)temp);
}
else
{
char temp[30];
sprintf(temp,"error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
}
rx_flag = 0;memset(data,0,30); //执行完接受记得把接收字符串数组和数组标记清零
}
串口的发送:
char temp[30];
sprintf(temp,"hello world\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
串口的发送就很简单,只需调用下面的函数即可;
*HAL_UART_Transmit(&huart1,(uint8_t )temp,strlen(temp),50);
其中,只是一个大概的总结,推荐一个博主,基本全面
链接: https://blog.csdn.net/qq_63856395/category_12244665.html