前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >蓝桥杯嵌入式之扩展板数码管、ADC按键讲解

蓝桥杯嵌入式之扩展板数码管、ADC按键讲解

作者头像
用户5935416
发布2019-08-01 10:10:56
9430
发布2019-08-01 10:10:56
举报
文章被收录于专栏:IT界的小白帽IT界的小白帽

扩展板中的数码管讲解

蓝桥杯嵌入式扩展板有三个共阴数码管,用3个74LS595来控制。74LS595是串/并转换,在时钟信号的作用下,将串口输入的8个高低电平锁存到芯片中并并行输出。如同单片机中的LED点阵。其电路图为

相应的跳线帽链接如图所示

其P3口和P4口的1、2、3要用跳线帽相连。真实实物连接方法如下

数码管的初始化代码为

代码语言:javascript
复制
void STM3210B_SEG_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

}

将PA1、PA2、PA3设置为上拉输出。在比赛时可以根据电路图确定需要设置的是哪个I/O口。

数码管显示代码为

代码语言:javascript
复制
void SEG_DisplayValue(u8 Bit1, u8 Bit2, u8 Bit3)
{
  u8 i = 0;  //
  u8 code_tmp = 0;

  code_tmp = Seg7[Bit3];
  for(i=0;i<8;i++)
  {
    if(code_tmp & 0x80)SER_H;
    else SER_L;
    SCK_H;
    code_tmp = code_tmp << 1;   
    SCK_L;
  }
  
  code_tmp = Seg7[Bit2];
  for(i=0;i<8;i++)
  {
    if(code_tmp & 0x80)SER_H;
    else SER_L;
    SCK_H;
    code_tmp = code_tmp << 1;   
    SCK_L;
  }  
  
  code_tmp = Seg7[Bit1];
  for(i=0;i<8;i++)
  {
    if(code_tmp & 0x80)SER_H;
    else SER_L;
    SCK_H;
    code_tmp = code_tmp << 1;   
    SCK_L;
  }    
  RCLK_H;
  RCLK_L;
}

其中SER_H、SER_L、SCK_H、SCK_L、RCLK_H、RCLK_L的定义是

代码语言:javascript
复制
#define RCLK_H      GPIO_SetBits(GPIOA,GPIO_Pin_2)
#define RCLK_L      GPIO_ResetBits(GPIOA,GPIO_Pin_2)

#define SER_H      GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define SER_L      GPIO_ResetBits(GPIOA,GPIO_Pin_1)

#define SCK_H      GPIO_SetBits(GPIOA,GPIO_Pin_3)
#define SCK_L      GPIO_ResetBits(GPIOA,GPIO_Pin_3)

在相应的.h文件中定义。

函数中的参数决定数码管显示的内容,第一个参数决定第一个数码管的显示,第二个参数决定第二个数码管的显示......因为三个74LS595是通过级联的方式连接,即第一个的串行输出口QH‘与第二个74LS595的串口输入端SER相连。所以需要先将第三个的参数进行传递,再传递第二个参数,最后传递第一个参数。

变量code_tmp 的值是由参数根据数组Seg7确定的。数组Seg7保存的是数码管显示各种字符或数字的十六进制数。数组Seg7的定义为

代码语言:javascript
复制
uc8 Seg7[17] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x4f,0x79,0x78,0x00};

这个需要自己提前推导。

语句if(code_tmp & 0x80)是判断变量code_tmp的最高位是否为1,如果为1,则将串口输入的电平拉高,否则拉低。由时钟信号的下降沿(由高电平跳变为低电平)锁存并输出到并行端口。其中为了让下降沿变得明显,所以在高低电平变化中添加变量code_tmp的移位命令,使时钟信号的高电平时间长一点,让芯片能够很好的捕捉到时钟信号的下降沿。也可以用延时来增加高电平的时间,但是最后还得需要对变量的移位code_tmp。变量code_tmp的移位是将每一位移到最高位,需要注意的是移到最高位需要左移函数。

ADC按键讲解

ADC按键的电路图为

ADC采集的电压为电容C的电压,当有不同的按键按下的话会有不同的电压值,对应电压值的不同计算出哪个按键按下。

对应的跳线帽为

ADC按键初始化代码为

代码语言:javascript
复制
void ADC_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  ADC_InitTypeDef ADC_InitStructure;
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  // ADC1 工作模式配置
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //单次转换
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 5;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_13Cycles5);

  ADC_Cmd(ADC1, ENABLE);   
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
}

与普通ADC初始化的区别在于结构体ADC_InitStructure的元素ADC_NbrOfChannel的值为5。GPIO口为PA5。所选通道为5,即ADC_Channel_5。其它代码都与普通ADC的一样。

ADC的采集

因为ADC的值是时刻在发生变化的,所以需要多次采集ADC的值,其中间的值为ADC采集的值。本例为采集50次。代码为

代码语言:javascript
复制
u16 Read_Btn(void)
{
  u16 tmp;
  u8 i = 0,j = 0;
  
  for(i=0; i<BTN_BUFF_LEN; i++)
  {
    btn_buff[i] = Read_ADC();
  }

  for(i=0; i<=BTN_BUFF_LEN/2; i++)
  {
    for(j=0; j<  BTN_BUFF_LEN-i-1; j++)
    {
      if(btn_buff[j+1] < btn_buff[j])
        {
        tmp = btn_buff[j+1];
        btn_buff[j+1] = btn_buff[j];
        btn_buff[j] = tmp;
      }
    }
  }

  if(BTN_BUFF_LEN % 2 == 0)
  {
    return(btn_buff[BTN_BUFF_LEN/2-1] + btn_buff[BTN_BUFF_LEN/2])/2;
  }
  else
  {
    return(btn_buff[BTN_BUFF_LEN/2]);
  }
}

将采取的值放到一个数组btn_buff里,数组btn_buff的定义为

代码语言:javascript
复制
static u16 btn_buff[BTN_BUFF_LEN];

ADC值读取函数为

代码语言:javascript
复制
u16 Read_ADC(void)
{
  u16 ADC_VALUE = 0;
  
  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
  while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
  ADC_VALUE = ADC_GetConversionValue(ADC1);
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);

  return ADC_VALUE;
}

与普通ADC的区别在于读取的是2个字节的整型值,并且没有公式的转换。在软件打开后需要判断ADC的标志量的值,最后也要对标志量进行清除。

前一个for循环是读取50个ADC采样值,第二个for循环是对采样后的值进行从小到大排序,最后判断采集的值是否为偶数个。若是,则取中间两个值的平均数,否则取中间那个数。

常量BTN_BUFF_LEN的值是可以变化的,但是不能太小,否则读取到的采样值误差太大;也不能太多,否则按键读取速度慢。在这里变量BTN_BUFF_LEN的定义为

代码语言:javascript
复制
#define BTN_BUFF_LEN  50

按键转换函数

在读取完ADC的样值后需要转换成按键值,代码为

代码语言:javascript
复制
u8 Scan_Btn(void)
{
  u16 btn_tmp = 0;

  btn_tmp = Read_Btn();

  if(btn_tmp <= 0x0020)
  {
    return 1;
  }
  else if((btn_tmp >= 0x00B0) && (btn_tmp <= 0x0100))
  {
    return 2;
  }
  else if((btn_tmp >= 0x0240) && (btn_tmp <= 0x0300))
  {
    return 3;
  }
  else if((btn_tmp >= 0x03B0) && (btn_tmp <= 0x0450))
  {
    return 4;
  }
  else if((btn_tmp >= 0x0500) && (btn_tmp <= 0x0600))
  {
    return 5;
  }
  else if((btn_tmp >= 0x0700) && (btn_tmp <= 0x0800))
  {
    return 6;
  }
  else if((btn_tmp >= 0x0840) && (btn_tmp <= 0x0940))
  {
    return 7;
  }
  else if(btn_tmp <= 0x0B50)
  {
    return 8;
  }
  else
  {
    return 0;  //error status & no key
  }
}

样值在某一区间的话,则被判为某一按键被按下。类似于ADC的抽样、量化、编码步骤。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT界的小白帽 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档