蓝桥杯嵌入式扩展板使用
目录
使用说明
写这个的意图
- 记录一下CubeMX的引脚和开发板跳线帽的连接。实在被这个烦的不行!
- 把每个模块的代码固定下来,这样可以节省点时间。
我目前的情况
- 我使用的是
STM32G431RBT6
- 省一但且名次靠后。
- 目前还在学习扩展板。
数码管使用
原理
- 板子上的是共阴数码管,高电平点亮。
- 控制芯片是SN74LS595N,这样就可以用三个引脚来控制三个数码管了。
- 分别有三个我们会用到的引脚:
RCLK
、SCK
、SER
- 工作流程是这样的:
首先我们需要把我们要输入的数据0\1
放在SER
引脚上,然后给SCK
一个上升沿的跳变,这样SER
的数据就传入移位寄存器了,我们连线进行8此这样的操作,8位数码管的数据就存在移位寄存器了,里面了,如果我们再往里面移数据,数据就会溢出这样三个74LS595
串联就是24位的移位寄存器了,我们就进行24次数据存储,存储的是三个数码管的数据,这时候移位寄存器的数据还没有输出。我们给RCLK
一个上升沿,数据一起输出,这样就实现了3个引脚控制三个数码管了。备赛
- 引脚:
网络标签 芯片引脚 RCLK PA2 SCK PA3 SER PA1
能够自己手写数码管驱动代码
数码管显示的编码:
显示字符 0 1 2 3 4 5 6 7 8 9 A B C D E F None 段码 0x3f 0x06 0x5b 0x4f 0x66 0x6d 0x7d 0x07 0x7f 0x6f 0x77 0x7c 0x39 0x5e 0x79 0x71 0x00
- 代码
//0、1、2、3、4、5、6、7、8、9、10(A)、11(B)、12(C)、13(D)、14(E)、15(F) uint8_t Nixie_Number[17] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x00}; void Nixie_Tube_Show(uint8_t D_1,uint8_t D_2,uint8_t D_3)//输入的是0~16的数值 { HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); uint8_t Data[3] = {D_3,D_2,D_1}; for( uint8_t i = 0;i<3;i++ ) { for( uint8_t j=0;j<8;j++ ) { HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET); HAL_Delay(1); if( Nixie_Number[Data[i]] & (0x80>>j) ) HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET); else HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET); HAL_Delay(1); } } HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET); HAL_Delay(1); }
ADC按键
原理
- 通过检测
PA5:ADC_KEY
的电压来确定是哪一个按键杯被按下。但是这个电压范围确定下来确实是一个麻烦事。备赛
- 引脚:
接线柱 芯片引脚 ADC_KEY PA5
- 要记住每个按键的ADC取值范围,我在这里首先取50次的ADC采集的值,就是直接读数据寄存器的值。然后对这50次的值排序求中位数,不知道为什么不这样捕获的值好像不是很好区分(蓝桥官方是这样写)。然后用
100
为基数来进行比较判断是哪一个按键按下。如果没检测到按键就说按键没有按下。
按键 ADC采集寄存器值(取50次中位数) 记忆编程值 实际ADC采集上限范围 K1 0 2 = 2+6*0 200 = 2*100 K2 479 8 = 2+6*1 800 = 8*100 K3 1089 14 = 2+6*2 1400 = 14*100 K4 1689 20 = 2+6*3 2000 = 20*100 K5 2297 26 = 2+6*4 2600 = 20*100 K6 2816 32 = 2+6*5 3200 = 32*100 K7 3440 38 = 2+6*6 3800 = 38*100 K8 3914 40 4000
- 接下来我把我捕获ADC采集值后,根据上面的判断值判断是那个按键按下的代码拿上来。
#define KEY_COUNT 50//采集次数 uint8_t adc_achive_flag = 0;//用来标记是否转换完成 uint32_t adc_buf;//用来存储ACD采集值 float KEY_V[KEY_COUNT] = {0};//存取多个采集值的数组 uint16_t Read_ADC_KEY_Val(void)//读取最终读取的ADC采集值 { uint16_t key_val; for(uint8_t i =0;i<KEY_COUNT;i++)//多次采集 { if( adc_achive_flag == 0 ) { adc_achive_flag = 1; KEY_V[i] = adc_buf; HAL_ADC_Start_DMA(&hadc2,&adc_buf,1); } } uint16_t temp; for(uint8_t i = 0;i<KEY_COUNT;i++)//排序 { for(uint8_t j=i;j<KEY_COUNT;j++) { if( KEY_V[j]>KEY_V[j+1] ) { temp = KEY_V[j]; KEY_V[j] = KEY_V[j+1]; KEY_V[j+1] = temp; } } } key_val = (KEY_V[KEY_COUNT/2-1]+KEY_V[KEY_COUNT/2])/2; return key_val; } uint8_t While_Scan_ADC_Key(void) { uint16_t key_val = Read_ADC_KEY_Val();//获取ADC采集数据后,数据寄存器的值 uint16_t Compare_Val = 100;//比较值计数 if( key_val < Compare_Val*(2+6*0) ) { return 1;//按键1按下 } else if( key_val < Compare_Val*(2+6*1) ) { return 2;//按键1按下 } else if( key_val < Compare_Val*(2+6*2) ) { return 3;//按键1按下 } else if( key_val < Compare_Val*(2+6*3) ) { return 4;//按键1按下 } else if( key_val < Compare_Val*(2+6*4) ) { return 5;//按键1按下 } else if( key_val < Compare_Val*(2+6*5) ) { return 6;//按键1按下 } else if( key_val < Compare_Val*(2+6*6) ) { return 7;//按键1按下 } else if( key_val < Compare_Val*(40) ) { return 8;//按键1按下 } else { return 0;//没有按键按下 } } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)//ADC中断回调函数 { if( hadc == &hadc2 ) { if( adc_achive_flag == 1 ) { adc_achive_flag = 0; HAL_ADC_Stop_DMA(&hadc2);//停止转换 } } }
单ADC双通道采集
原理
- 原理很简单,就是和之前采集一样。由于用ADC的双通道采集功能在蓝桥的板子上会两个通道之间会有干扰,所以我们还是采用单通道的方式来采集,只是在采集不同通道时,重新配置采集通道而已。
备赛
- 引脚
扩展板丝印 原理图引脚标签 芯片引脚 ADC2的采集通道 RP5 AO1 PA4 CHANNEL_17 RP6 AO2 PA5 CHANNEL_13
- 直接贴代码了,以后我就这样写,虽然没有DMA那样快,但是这样在这里单纯读电压值,是没有影响的。但是对于ADC按键就不要这样了,因为那个有点延迟的感觉,就是响应有点慢。
//通道枚举 typedef enum { CHANNEL_17 = 17, CHANNEL_13 = 13 }ADC_Channel; //根据通道直接读出ADC转换出来的寄存器值。 uint16_t Read_ADC_Channel(ADC_Channel Channel) { //从adc.c里面复制 ADC_ChannelConfTypeDef sConfig = {0}; if( Channel == 17 ) { sConfig.Channel = ADC_CHANNEL_17; } else { sConfig.Channel = ADC_CHANNEL_13; } sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) { Error_Handler(); } //开启转换,直接读值,没有影响 HAL_ADC_Start(&hadc2); uint16_t val = HAL_ADC_GetValue(&hadc2); HAL_ADC_Stop(&hadc2); return val; }
PWM捕获
原理
- PWM捕获分两部分,一部分是频率捕获测量,一部分是脉宽捕获测量。由于还是32的片上外设,这个过了省赛就比较熟悉了,所以就只是简述一下,有这个东西。
- 频率捕获测量:
频率捕获使用使用的是RP3
和RP4
,和省赛是一样的测量方法。- 脉宽测量:
这个其实就是一个PWM的高电平持续时间,由于脉宽我们一般以占空比的形式表示,所以最后根据题目转换成占空比,还是直接使用。备赛
引脚
捕获功能 开发板旋钮 定时器 通道 引脚 接线柱 频率捕获1 RP3 tim2 CHANNEL_2 PA1 PULS1 频率捕获2 RP4 tim2 CHANNEL_3 PA2 PULS2 脉宽捕获1 RP1 tim3 CHANNEL_1 PA6 PWM1 脉宽捕获1 RP2 tim3 CHANNEL_2 PA7 PWM2 代码
void PWM_Init(void)//模块初始化 { HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1); __HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING); __HAL_TIM_SET_COUNTER(&htim3,0); } uint8_t capture_flag_1 = 0; uint32_t PWM_F_1,PWM_D_1; uint32_t PWM_T_Count_1; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//中断回调函数 { if( htim == &htim3 ) { if( capture_flag_1 == 0 )//频率计算 { capture_flag_1 = 1; __HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING); PWM_T_Count_1 = __HAL_TIM_GET_COUNTER(&htim3); PWM_F_1 = 80000000/80/PWM_T_Count_1; __HAL_TIM_SET_COUNTER(&htim3,0); } else if(capturea_flag_1 == 1)//脉宽计算 { capture_flag_1 = 0; __HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING); PWM_D_1 = __HAL_TIM_GET_COMPARE(&htim3,TIM_CHANNEL_1)*100/PWM_T_Count_1; } } }
温度传感器
原理
- 了解,国赛不给代码就在想办法。
- 单总线时序要求非常严格,自己写代码最好看着芯片手册写,因为每种的单片机的性能不同,这样就需要我们调试信号维持时间,我记得当时我是搞的头皮发麻,因为我比较菜。
备赛
- 引脚
接线柱 芯片引脚 TDQ PA6
- 会用官方给的读寄存器函数,然后自己做数据转换,还有就是把
lcd.h
里面的数据类型复制到ds18b20.h
,因为官方的代码要用。还有就是把延时函数定义成static
要不在其他文件里面可能重定义。- 代码
typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; typedef __IO uint32_t vu32; typedef __IO uint16_t vu16; typedef __IO uint8_t vu8; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef const uint32_t uc32; /*!< Read Only */ typedef const uint16_t uc16; /*!< Read Only */ typedef const uint8_t uc8; /*!< Read Only */
static void delay(unsigned int n) { while(n--); }
float Read_Temp(void)//温度读取 { //取低11位 float T = (float)(ds18b20_read()&0x7FF)/16.0f; return T; }
温湿度传感器
原理
- 这个应该也是给代码,不给代码就在想办法,我只要标注一下引脚
备赛
- 引脚
接线柱 芯片引脚 HDQ PA7
- 把延时函数定义成
static
。- 把
lcd.h
里面的数据类型复制到ds18b20.h
。- 代码
typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; typedef __IO uint32_t vu32; typedef __IO uint16_t vu16; typedef __IO uint8_t vu8; typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; typedef const uint32_t uc32; /*!< Read Only */ typedef const uint16_t uc16; /*!< Read Only */ typedef const uint8_t uc8; /*!< Read Only */
static void delay(unsigned int n) { while(n--); }
上述代码地址
代码地址
提取码:2xq3