第十二届蓝桥杯嵌入式备考
写在最前面
写这篇文章的目的是方便自己写代码的时候看看有没有漏掉那个步骤,所以没有特别注意介绍每个部分的原理,望见谅。
这篇文章最好用的用法是用Typora
打开,有需要可以访问https://github.com/Wurlitze/12-.git.
1.LED配置
LED的配置很简单,把PC8-15
和PD2
配置为GPIO_Output
。(PD2
是锁存器,高电平开,低电平关;PC8-15
对应板子上LD1-8
)
1.1GPIO HAL库函数
HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_8); //读取PC8的电平
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); //写PD2的电平为高
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8); //翻转PC8的电平
1.2LED_Disp
void LED_Disp(uint8_t ucled)
{
GPIOC->ODR = ~ucled<<8; //这里做电平翻转方便调用时高电平代表亮
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
1.2.1常用的LED操作
uint8_t ucled = 4;
LED_Disp(ucled); //LD3亮,其他灭
LED_Disp(~ucled); //LD3灭,其他亮
LED_Disp(ucled ^=4); //单独让LD3翻转
LED_Disp(ucled &= ~(1<<1)); //单独让LD2灭
LED_Disp(ucled |= 1<<1); //单独让LD2亮
2.按键配置
将PB0-2
和PA0
配置为GPIO_Input
.由于Key1
为PB0
,Key4
为PA0
,两个的中断通道相同,所以用中断不能实现四个按键只能配置Key1-3
。所以多用按键扫描的方式。
2.1Key_Scan 按键扫描
uint8_t Key_Scan(void)
{
uint8_t Key_Val;
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0))
{
HAL_Delay(50);
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0))
Key_Val = 1;
}
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1))
{
HAL_Delay(50);
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1))
Key_Val = 2;
}
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2))
{
HAL_Delay(50);
if(!HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2))
Key_Val = 3;
}
if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
{
HAL_Delay(50);
if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
Key_Val = 4;
}
return Key_Val;
}
2.2 Key_Proc 按键处理函数
unsigned char Key_Long;
unsigned long ucTick_ms,KeyTick_Old;
void Key_Proc(void)
{
unsigned char Key_Val;
Key_Val = Key_Scan();
if(Key_Val != Key_Long)
{
Key_Long = Key_Val;
KeyTick_Old = ucTick_ms;
}
else
{
Key_Val = 0;
}
switch(Key_Val) //单击事件处理
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
}
if(ucTick_ms - KeyTick_Old > 1000) //长按时间
switch(Key_Long) //长按事件处理
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
}
}
2.3按键处理常见问题汇总
1.按键1
的作用一般是是页面切换,我习惯用一个布尔变量实现,然后在LCD处理函数里面写一个if
判断用来实现两个页面的数据显示。每次切换都清一次屏就可以避免有些地方没有更新到带来的残留。如果某些按键要求在设置页面有效,需要在case之后写一个判断
_Bool LCD_Flag;
//按键处理函数里面
switch(Key_Val)
{
case 1:
LCD_Flag ^= 1;
LCD_Clear(Black);
break;
case 2:
if(LCD_Flag)
{
}
break;
2.长按和短按case
里面的内容是一样的,不需要改些什么。
3.LCD配置
- LCD不需要配置CubeMX,只需把
lcd.c
导入Src
文件夹、lcd.h
和fonts.h
导入Inc
文件夹即可。在Keil里面添加.c文件,然后在main.c
中引用
#include "lcd.h"
- 在main函数中初始化即可
LCD_Init();
3.1常用LCD函数
LCD_Clear(White); //用白色清屏
LCD_SetBackColor(White); //设置背景为白色
LCD_SetTextColor(Blue); //设置文本为蓝色
LCD_DisplayStringLine(Line0,(uint8_t *)" Main "); //显示第0行
//显示单个字符的时候要设置行和列,列要倒着计算最左边为320,最右边为16.
LCD_DisplayChar(Line3,192,Time[0]/10+0x30); //显示单个字符(数字要加0x30)
LCD_DisplayChar(Line3,160,':'); //显示特殊符号
//设置高亮
if(j == 0) LCD_SetTextColor(Red);
LCD_DisplayChar(Line3,192,Time[0]/10+0x30);
LCD_DisplayChar(Line3,176,Time[0]%10+0x30);
LCD_SetTextColor(Blue);
LCD_DisplayChar(Line3,160,':');
3.2LCD常见问题汇总
1.单个数字显示的时候记得转换为acsii码(如上),位置计算公式(从左到右320-16*n)。
4.ADC配置
- 一般使用
R37
,既配置PB15
为ADC2_IN15
,其他默认即可,不用修改。
- 在
adc.c
中添加一个getADC();
也可以直接写在main.c
里,这样就不用再添加声明了。
uint16_t getADC(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc2);
adc = HAL_ADC_GetValue(&hadc2);
return adc;
}
adc.h
中添加声明,方便外部引用该函数。
uint16_t getADC(void);
- ADC处理函数
unsigned int uiAdc_Val;
unsigned char pucStr[21]; //方便使用sprintf函数
void ADC_Proc(void)
{
float temp;
uiAdc_Val = getADC();
temp = (float)(uiAdc_Val*3.3/4096);
sprintf((char*)pucStr," V:%0.2fV ",temp);
LCD_DisplayStringLine(Line2, pucStr);
}
4.1双通道ADC配置
双通道ADC一般是拓展板上测量两路ADC时用的,因为是ADC2的两个通道,所以不能使用两个ADC来测量
- 配置两路ADC的引脚
- 添加一路DMA,配置如下
- 开启连续转换,DMA请求,把通道数改为2.
- 在主函数中添加开启DMA函数,接收到的数据存放在
ADC_ReBuf
中
uint32_t ADC_ReBuf[200],ADC_Val[2];
HAL_ADC_Start_DMA(&hadc2,ADC_ReBuf,200);
- 处理接收到的数据,两个通道的数据是交替保存的,将得到的100的值取均值(均值滤波)。
for(i = 0;i< 100;i++)
{
ADC_Val[0] += ADC_ReBuf[i*2];
ADC_Val[1] += ADC_ReBuf[i*2+1];
}
ADC_Val[0] /= 100;
ADC_Val[1] /= 100;
- 这里的ADC_Val就是最后得到的值(0~4096),对得到的值进行处理
Temp1 = (float)ADC_Val[0]*3.3f/4096;
Temp2 = (float)ADC_Val[1]*3.3f/4096;
4.1ADC常见问题总结
- 数字滤波(意思一下而已)
float temp, temp1;
unsigned int uiAdc_Val;
unsigned char pucStr[21]; //方便使用sprintf函数
void ADC_Proc(void)
{
uiAdc_Val = getADC();
temp1 = temp;
temp = (float)(uiAdc_Val*3.3/4096);
sprintf((char*)pucStr," V:%0.2fV ",temp*0.8f+temp1*0.2f);
LCD_DisplayStringLine(Line2, pucStr);
}
5.TIM定时器配置
定时器频率计算
5.1PWM配置
5.1.1单PWM波配置
- CubeMx配置
预分频系数Prescaler
和自动重装载值ARR
决定PWM波的频率,Pulse
决定PWM的占空比。
- 在
main
函数里开启PWM通道
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
5.1.2可调频率及占空比PWM波配置
- 调节占空比
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Duty_Val[1]*100); //设置比较值
- 调节频率
HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
__HAL_TIM_SET_AUTORELOAD(&htim2,9999);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,5000);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
TIM2->CNT = 0; //清空CNT,有一次debug发现第二次配置ARR的时候CNT的值会跑飞
5.2输出捕获配置
这个模式主要用于单个定时器生成多路频率占空比都不相同的PWM波。
核心思想是通过不断更新比较值来翻转电平模拟PWM。
- 一般不配置
ARR
的值,Pusle
的值都可,因为后面会一直变。
1.CubeMX配置
- 模式配置为Toggle on match
- 重写callback函数
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
__IO uint16_t count;
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
count = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
if(PA6 ^= 1)
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,count+Duty_Val[1]*100);
else
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,count+CCR1_Val-(Duty_Val[1]*100));
}
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
count = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_2);
if(PA7 ^= 1)
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,count+Duty_Val[2]*100);
else
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,count+CCR2_Val-(Duty_Val[2]*100));
}
}
- 在
main
函数中开启中断
HAL_TIM_OC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_OC_Start_IT(&htim3,TIM_CHANNEL_2);
5.3输入捕获配置
- CubeMX配置
- 系统时钟为80MHz,预分频系数设置为79,自动重装载值设置为0(就是这里的0xffffffff),这样一个计数值的时间为1μs。
- 这里设置为双边沿触发,目的是为了方便测占空比,如果只是测频率,可以设置为上升沿或下降沿触发。
- 输入滤波器是用于稳定波形的,在外接信号发生器的时候,输入容易不稳,可以按照需求设置大小。
- 使能中断
- 重写回调函数
- 这里的
ch2_Val
数组是用来保存计数值的,因为是双边沿触发,所以可以用两个差值为2的计数值来计算频率,100000是定时器的计数频率。 - 占空比这里没有特别深究是1的持续时间还是0的持续时间,算是一处缺陷吧,可以用交叉设置上升沿下降沿的方式来测得值为1的占空比。考虑到一般都是百分比,所以计算的时候直接乘了100。
int long ch2_Val[6], F40 = 0;
int Duty_cycle;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
ch2_Val[i++] = __HAL_TIM_GET_COUNTER(&htim2);
if(i>5)
{
i=0;
F40 = 1000000/(ch2_Val[4]-ch2_Val[2]);
Duty_cycle = (ch2_Val[3]-ch2_Val[2])*100/(ch2_Val[4]-ch2_Val[2]);
__HAL_TIM_SetCounter(&htim2,0);
}
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
}
- 在
main
函数中开启中断
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
5.3TIM定时器常见问题
- 官方给出的测频率的例子(适合单测频率),但是因为每次都赋0,会对测量精度产生一定的影响
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
Tim_Val = __HAL_TIM_GetCounter(&htim2);
FRQ_Val[0] = 1000000/Tim_Val;
__HAL_TIM_SetCounter(&htim2,0);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
}
6.USART串口配置
- CubeMX配置
需要注意,要手动配置PA9
和PA10
为USART1_TX
和USART1_RX
。开启中断并设置抢占优先级不为0(因为在中断处理函数中调用了延时函数,如果不设置优先级会导致程序卡死,如果没有调用延时可以配置优先级为0)。
- 重写fputc函数(重写之后才可使用
printf
)
int fputc(int c, FILE *stream) //重写fputc函数
{
/*
huart1是工具生成代码定义的UART1结构体,
如果以后要使用其他串口打印,只需要把这个结构体改成其他UART结构体。
*/
HAL_UART_Transmit(&huart1, (unsigned char *)&c, 1, 1000);
return 1;
}
- 在中断服务函数中添加延迟,(接收多个字节的时候不需要添加)。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
HAL_Delay(100); //接收多个字节的时候要注释掉
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
- 重写回调函数
uint8_t USART1_RX_CNT = 0, ucRxBuf;
char RxBuf[128], pucRcv[128];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(USART1_RX_CNT >= 127) //127+1是最大能接收到的数据长度
{
USART1_RX_CNT = 0;
memset(RxBuf,0x00,sizeof(RxBuf));
HAL_UART_Transmit(&huart1,(uint8_t *)"error",10,1000);
}
else
{
RxBuf[USART1_RX_CNT++] = ucRxBuf;
if((RxBuf[USART1_RX_CNT-1] == 0x0a ) && (RxBuf[USART1_RX_CNT-2] == 0x0d))
{
memcpy(pucRcv,RxBuf,sizeof(RxBuf));
USART1_RX_CNT = 0;
memset(RxBuf,0x00,sizeof(RxBuf));
}
}
HAL_UART_Receive_IT(&huart1,(uint8_t *)&ucRxBuf,1);
}
- 在main函数中添加串口中断函数
HAL_UART_Receive_IT(&huart1,(uint8_t *)&ucRxBuf,1);
- 接收到的数据保存在
pucRcv
中,处理完接收到数据之后要对数组清零
memset(pucRcv,0x00,sizeof(pucRcv));
6.1USART串口常见问题
- 在串口处理函数中经常会用到数组比较函数
memcmp
、数组赋值函数memset
等等,这些都是C语言的函数,写在在stdio.h
里,所以如果要是忘了可以在stdio.h
里查,里面还有相关函数介绍。 - 要注意接收到数字是ascii码,要先减
0X30
再使用。
- 举例如下(第六届模拟题)
if(memcmp(RxStr,"SET:",4) == 0)
{
if(RxStr[4] == 0x31)
{
DouFrq_Val[0] = RxStr[6]-0x30;
if(RxStr[9])
DouFrq_Val[0] = 10;
}
if(RxStr[4] == 0x32)
{
DouFrq_Val[1] = RxStr[6]-0x30;
if(RxStr[9])
DouFrq_Val[1] = 10;
}
printf("Set OK!\r\n");
memset(RxStr,0x00,sizeof(RxStr));
i2c_write(DouFrq_Val,0,2);
}
- 处理完接收数组,记得要对数组清零。
memset(RxStr,0x00,sizeof(RxStr));
7.EEPROM配置(i2c)
- CubeMX配置
将PB6
和PB7
配置成GPIO_Output
。用的是软件模拟i2c,所以不用配置i2c。但是要把i2c.c
添加到Src
,i2c.h
添加到Inc
. i2c_write()
;和i2c_Read()
;
/* E2PROM */
//
void i2c_read(unsigned char * pucBuf,unsigned char address,unsigned char ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(address);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
while(ucNum--)
{
*pucBuf++ = I2CReceiveByte();
if(ucNum)
I2CSendAck();
else
I2CSendNotAck();
}
I2CStop();
}
//
void i2c_write(unsigned char * pucBuf,unsigned char address,unsigned char ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(address);
I2CWaitAck();
while(ucNum--)
{
I2CSendByte(*pucBuf++);
I2CWaitAck();
}
I2CStop();
}
- 在
main
函数中初始化
I2CInit();
7.1 i2c常见问题
- 读写i2c示范
unsigned char pucTh[3];
i2c_read(pucTh,0,3);
i2c_write(pucTh,0,3);
unsigned char Time[5][3];
i2c_read(*Time,0,15);
i2c_write(*Time,0,15);//e2p存储
- 如果发现i2c读写没反应,首先就要检查是否配置
PB6
和PB7
。
- I2C的读写流程在
AT24C02.pdf
里面有
8.RTC配置
8.1使用sysTick配置
8.2使用RTC配置
- RTC的配置
- 这里吗没有开启闹钟,蓝桥杯的题里面很少有用到闹钟的。
Data Format
是时间表示的格式Binary data format
是十进制表示,BCD data format
是BCD码的形式表示。
- 获取时间以及显示时间,获取时间之后一定要获取一下日期,不然程序不会往下执行。
HAL_RTC_GetTime(&hrtc,&SysTime,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&SysDates,RTC_FORMAT_BIN);
sprintf((char *)pucStr," T:%02d:%02d:%02d ",SysTime.Hours,SysTime.Minutes,SysTime.Seconds);
LCD_DisplayStringLine(Line3,pucStr);
- 设置时间和设置日期
HAL_StatusTypeDef settime(uint8_t hours,uint8_t mins,uint8_t secs)
{
RTC_TimeTypeDef stime;
stime.Hours=hours;
stime.Minutes=mins;
stime.Seconds=secs;
//注意此处要用RTC_FORMAT_BIN十进制
return HAL_RTC_SetTime(&hrtc, &stime, RTC_FORMAT_BIN);
}
//设置日期
HAL_StatusTypeDef setdate(uint8_t years,uint8_t mons,uint8_t dates,uint8_t weeds)
{
RTC_DateTypeDef sdate;
sdate.Year=years;
sdate.Month=mons;
sdate.Date=dates;
sdate.WeekDay=weeds;
return HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BIN);
}
9.数码管配置
使用数码管需要将拓展板上的P4.1
~P4.3
短接到P3.1
~P3.3
- 配置引脚
PA1~3
9.1SEG显示函数
- 这个函数直接写在
mian.c
里面即可
void SEG_Disp(uint8_t ucData1, uint8_t ucData2, uint8_t ucData3, uint8_t ucDot)
{
uint8_t i;
uint8_t code[17] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71,0x00};
unsigned long ulData = (code[ucData3] << 16) + (code[ucData2] << 8) + code[ucData1];
ulData += (ucDot&1)<<23;
ulData += (ucDot&2)<<14;
ulData += (ucDot&4)<<5;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
for(i = 0; i<24 ; i++)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
if(ulData & 0x800000)
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
ulData <<= 1;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
}
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);
}
9.2显示实例
0X10
代表什么也不显示,0x01
代表显示0.
/* USER CODE BEGIN 2 */
SEG_Disp(0x10, 0x10, 0x10, 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
SEG_Disp(i,i+1,i+2,0);
if(i+2 == 0x10)
i = 0x00;
else
i++;
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
10.ADC按键配置
- 连接拓展板
P4.5
到P5.5
,配置PA5为一路ADC通道。
10.1 Key_Scan (ADC版)
- 这里
ADC
的值需要自行测量,每个按键按下对应的值会因为板子的不同而不同。
uint8_t Key_Scan(void)
{
uint8_t ucAKey_Val;
ADC_Val[0] = getADC();
if(ADC_Val[0] < 3991)
{
HAL_Delay(100);
if(ADC_Val[0] < 3991)
{
if(ADC_Val[0] > 3840)
ucAKey_Val = 8;
else if(ADC_Val[0] > 3080)
ucAKey_Val = 7;
else if(ADC_Val[0] > 2522)
ucAKey_Val = 6;
else if(ADC_Val[0] > 1978)
ucAKey_Val = 5;
else if(ADC_Val[0] > 1384)
ucAKey_Val = 4;
else if(ADC_Val[0] > 784)
ucAKey_Val = 3;
else if(ADC_Val[0] > 240)
ucAKey_Val = 2;
else
ucAKey_Val = 1;
}
}
return ucAKey_Val;
}
10.2 Key_Proc 按键处理函数
- 和前面的
Key_Proc
一样,只是判断值增加到了8
.
void Key_Proc(void)
{
uint8_t Key_Val;
Key_Val = Key_Scan();
if(Key_Val != Key_Long)
{
Key_Long = Key_Val;
Tick_old = ucTick_ms;
}
else
Key_Val = 0;
switch(Key_Val)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
}
if(ucTick_ms - Tick_old > 800)
{
switch(Key_Val)
{
}
}
}
11.DHT11温湿度传感器配置
- 连接
P4.7
到P3.7
,配置PA7引脚。添加CHT11.c
到src
文件夹中,CHT11.h
到inc
文件夹中.
- 在
main.c
中添加以下代码。
u8 temper ;
u8 humi ;
while(DHT11_Init()) //DHT11初始化
{
LCD_DisplayStringLine(Line5, (uint8_t *)" DHT11 Error ");
HAL_Delay(200);
}
while (1)
{
/* USER CODE END WHILE */
DHT11_Read_Data(&temper, & humi); //读取温湿度值
snprintf((char *)str, sizeof(str), " TH&RH:%d, %d ", temper, humi);
HAL_Delay(200);
LCD_DisplayStringLine(Line6, str);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
12.DS18B20温度传感器配置
- 连接
P4.6
到P3.6
,配置PA6引脚。添加DS18B20.c
到src
文件夹中,DS18B20.h
到inc
文件夹中. - 在
main.c
中添加以下代码。
float tem = 0.0;
u16 read = 0;
/* USER CODE BEGIN 2 */
ds18b20_init_x();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
read = (ds18b20_read() & 0x07FF);
tem = read / 16.;
snprintf((char *)str, sizeof(str), " Temperatrue:%2.2f" , tem);
LCD_DisplayStringLine(Line6, str);
HAL_Delay(200);
}
13.LIS302DL加速度传感器配置
- 这个传感器其实是用来测倾斜角的,听往届的大佬说考的不多,因为需要拿板子旋转,考场会很乱。
- 修改
i2c.c
的两个引脚,数据线改为PA5
,时钟线改为PA4
.添加LIS302DL.c
到src
文件夹中,LIS302DL.h
到inc
文件夹中(名字也有可能叫mems).
#include "i2c.h"
#define DELAY_TIME 500
/** I2C 总线接口 */
#define I2C_PORT GPIOA
#define SDA_Pin GPIO_PIN_5
#define SCL_Pin GPIO_PIN_4
#define FAILURE 0
#define SUCCESS 1
//配置SDA信号线为输入模式
void SDA_Input_Mode(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(I2C_PORT, &GPIO_InitStruct);
}
//配置SDA信号线为输出模式
void SDA_Output_Mode(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = SDA_Pin;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD ;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(I2C_PORT, &GPIO_InitStruct);
}
-
连接
P2.1
-P2.2
,P2.3
-P2.4
;断开P4.4
和P4.5
,配置PA4
,PA5
引脚.
-
在
main.c
中添加以下代码。
s8 *ptr;
float x, y, z;
/* USER CODE BEGIN 2 */
LIS302DL_Config();
/* USER CODE END 2 */
while(LIS302DL_Check() == 0)
{
LCD_DisplayStringLine(Line5, (u8 *)" MEMS STATUS: ERROR");
}
LCD_DisplayStringLine(Line5, (u8 *)" MEMS STATUS: OK ");
while (1)
{
/* USER CODE END WHILE */
ptr = Lis302DL_Output();
x = ptr[0] * 18. / 1000;
y = ptr[1] * 18. / 1000;
z = ptr[2] * 18. / 1000;
snprintf((char *)str, sizeof(str), " X acclr:%.2fg ", x);
LCD_DisplayStringLine(Line6, str);
snprintf((char *)str, sizeof(str), " Y acclr:%.2fg ", y);
LCD_DisplayStringLine(Line7, str);
snprintf((char *)str, sizeof(str), " Z acclr:%.2fg ", z);
LCD_DisplayStringLine(Line8, str);
HAL_Delay(200);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
14.光敏电阻配置
- 有两个,一个只能输出0和1(光敏电阻数字输出
DO
),另外一个可以输出具体的光照值(光敏电阻模拟输出AO
)。 - 其中
P3.3
连接DO
,P3.4
连接AO
。
- 引脚配置
2.实现代码
//DO
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET){
LCD_DisplayStringLine(Line7, (u8*)" DO:High ");
}else{
LCD_DisplayStringLine(Line7, (u8*)" DO:Low ");
}
//AO
tmp = getADC();
snprintf((char *)str, sizeof(str), " R-P:%.2fK ", tmp/(4096.-tmp)*10);
LCD_DisplayStringLine(Line6, str);
HAL_Delay(200);