进入国赛以后,拓展板也是比较重要的一部分。上面的有些东西也是第一次接触,所以借这个机会把学习的过程记录下来,帮助大家排雷。
拓展板上资源大致分为:数码管、ADC按键、光敏电阻、温度传感器、温湿度传感器、三轴加速度传感器以及两路占空比可调PWM、两路频率可调PWM(用于输入捕获)和两路ADC输入。
看起来很多,其实PWM和模拟信号输出部分不需要编程硬件就实现了,捕获和ADC利用之前的知识就可以。按键和光敏电阻也是基于ADC部分的原理稍作修改,这样算下来其实任务就简化了,学起来也轻松多了。
拓展板一共有三个数码管,通过74LS595移位寄存器实现的。配置完对SER(串行数据输入引脚)、SCK(串行移位时钟输入引脚)、RCK(穿行存储移位时钟输入)进行操作就能实现。
个人理解三个引脚的用途:SER是对数码管位进行操作,可以理解成控制A-G某一段点亮或者熄灭
SCK用于移位,SCK下降沿的时候数据发生移位有效
RCK则是在下降沿将移位后的数据发送到硬件。
SER、RCK、SCK分别对应PA1、PA2、PA3,配置推挽输出模式即可
预定义宏
#ifndef _SEG_H_
#define _SEG_H_
#include "stm32f10x.h"
#define SER_H GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define SER_L GPIO_ResetBits(GPIOA,GPIO_Pin_1)
#define RCK_H GPIO_SetBits(GPIOA,GPIO_Pin_2)
#define RCK_L GPIO_ResetBits(GPIOA,GPIO_Pin_2)
#define SCK_H GPIO_SetBits(GPIOA,GPIO_Pin_3)
#define SCK_L GPIO_ResetBits(GPIOA,GPIO_Pin_3)
void Seg_Init(void);
void Seg_Write(u8 N1,u8 N2,u8 N3);
#endif
函数
#include "seg.h"
u8 Seg7[17] = { 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71,0x00};
void 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_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Seg_Write(u8 N1,u8 N2,u8 N3)
{
u8 num,i;
num = Seg7[N3];
for(i=0;i<8;i++)
{
if(num&0x80) SER_H;
else SER_L;
SCK_H;
num <<= 1;
SCK_L;
}
num = Seg7[N2];
for(i=0;i<8;i++)
{
if(num&0x80) SER_H;
else SER_L;
SCK_H;
num <<= 1;
SCK_L;
}
num = Seg7[N1];
for(i=0;i<8;i++)
{
if(num&0x80) SER_H;
else SER_L;
SCK_H;
num <<= 1;
SCK_L;
}
RCK_H;
RCK_L;
}
ADC按键其实就是根据分压原理,可以把它看成ADC,但不能把它看成按键。其实就是只利用一个IO但是可以检测多个按键,达到节省资源的优势。(不知道国赛会不会直接拿他当按键用)
原理:每个按键按下由于分压阻值不同输入的模拟信号不同,只需要对输入模拟信号值判断在哪个范围即可。
预定义宏
#define AdcKey1 1
#define AdcKey2 2
#define AdcKey3 3
#define AdcKey4 4
#define AdcKey5 5
#define AdcKey6 6
#define AdcKey7 7
#define AdcKey8 8
函数
#include "adckey.h"
#include "stdio.h"
#include "lcd.h"
void ADCkey_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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 = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
u16 Get_Adc(void)
{
u16 Value;
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
Value=ADC_GetConversionValue(ADC1);
ADC_SoftwareStartConvCmd(ADC1, DISABLE);
return Value;
}
u8 Key_Compare(void)
{
u16 value;
u8 str[20];
value = Get_Adc();
sprintf((char*)str," Key_Val : %d ",value);
LCD_DisplayStringLine(Line6,str);
if(value<500) return AdcKey1;
else if(value>500&&value<1000) return AdcKey2;
else if(value>1000&&value<1500) return AdcKey3;
else if(value>1500&&value<2000) return AdcKey4;
else if(value>2000&&value<2800) return AdcKey5;
else if(value>2800&&value<3300) return AdcKey6;
else if(value>3300&&value<3800) return AdcKey7;
else return AdcKey8;
}
这个也挺简单的,所以和上面两个放一块儿。光敏电阻就算随着光强增加阻值变小,它接在一个分压电路上。阻值变化分压就会相应变化,再通过ADC采集它的数值。光敏电阻有两种输出,一个模拟量一个开关量。开关量其实就算通过一个比较器,同相输入端是采集的模拟量,反向端是PR7的滑动电阻设置的一个可变的数值进行比较,输出0/1。
跳线帽:P5的TRD0和TRA0与P4对应连接
更详细原理可以参考这个:
https://blog.csdn.net/weixin_44236302/article/details/104818618?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
#include "tr.h"
#include "stdio.h"
#include "lcd.h"
void Tr_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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 = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
void Get_TrVal(void)
{
u16 Value;
u8 str[20];
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
Value=ADC_GetConversionValue(ADC1);
ADC_SoftwareStartConvCmd(ADC1, DISABLE);
sprintf((char*)str," TrAo_Val : %.2f ",(float)3.3*Value/4095);
LCD_DisplayStringLine(Line3,str);
if(TrDo==1)
sprintf((char*)str," TrDo : High ");
else
sprintf((char*)str," TrDo : Low ");
LCD_DisplayStringLine(Line5,str);
}
ADC按键从代码其实也可以看出我把ADC的值显示出来了,这样大家按下每个按键大概就能自己划定检测按键的范围了。相比直接把区间背下来,这样就算换了别的板子也更稳定,调试起来也很快。
还有一个问题,光敏电阻以后和ADC按键同时使用(通道4和通道5),是分别进行初始化的。但是出现了两路数据显示颠倒的问题,光敏电阻值显示的ADC按键值,按键按下改变的是光敏电阻显示的值。但如果两路单独运行其中一路即恢复正常。