一、数码管
- SEG数码管显示(PA1、PA2、PA3),
PA1——SER串行输入数据,PA2——RCK输出锁存时钟,PA3——SCK移位寄存器时钟上升 - 初始化:GPIO模式(PA1、PA2、PA3):GPIO_Mode_Out_PP推挽输出
时钟:GPIOA - 共阴极显示,即:高电平时,发光二极管点亮,(0:灭,1:亮)
- 每个数码管的8位二进制排序位【dp、g、f、e、d、c、b、a】
因为串行输入时,是从前往后输出,接受时,会将数据从后往前保存,即先接受dp位,将dp保存到最后面
因此,在设置输出到数码管的字段时,应该从后往前设置
关于串行输入是怎么操作的,不介绍,自行百度
因此,用数码管表示16进制的0~ F,使用如下数组,依次表示0~F以及全部熄灭uc8 Seg7[17] = { 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71,0x00};
使用代码如下:
(需记忆,是固定的)
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++){
//注意,这里使用的是串行输入SER,即每次只输入一位数据,也就是把8位二进制的首位给输入到数码管当中,因此,需要连续输入8次
//这里的输入指从PC机写入的数据输入到开发板
if(code_tmp & 0x80 ){//0x80二进制为1000 0000,即每次循环将code_tmp的首位与1作逻辑与
//
GPIO_SetBits(GPIOA,GPIO_Pin_1);//SER_H;如果code_tmp首位为1,那么,串行输入的SER_H值置为1
}else{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//SER_L;//如果code_tmp首位为0,那么,串行输入的SER_H值置为0
}
GPIO_SetBits(GPIOA,GPIO_Pin_3);//SCK_H;//移位寄存器打开
code_tmp = code_tmp << 1; //让code_tmp 左移一位,准备判断下一位的串行输入的数据
//每次左移一位,即将下一位串行输出的数据放到首位
GPIO_ResetBits(GPIOA,GPIO_Pin_3);//SCK_L;//移位后,重新把移位寄存器关闭,防止出现错误
}
//其他数码管的操作同上
code_tmp = Seg7[Bit2];//设置第二个译码管
for(i=0;i<8;i++){
if(code_tmp & 0x80){
GPIO_SetBits(GPIOA,GPIO_Pin_1);//SER_H;
}else{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//SER_L;
}
GPIO_SetBits(GPIOA,GPIO_Pin_3);//SCK_H;
code_tmp = code_tmp << 1;
GPIO_ResetBits(GPIOA,GPIO_Pin_3);//SCK_L;
}
code_tmp = Seg7[Bit1];//设置第一个译码管
for(i=0;i<8;i++){
if(code_tmp & 0x80){
GPIO_SetBits(GPIOA,GPIO_Pin_1);//SER_H;
}else{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//SER_L;
}
GPIO_SetBits(GPIOA,GPIO_Pin_3);//SCK_H;
code_tmp = code_tmp << 1;
GPIO_ResetBits(GPIOA,GPIO_Pin_3);//SCK_L;
}
//负责输出数据的锁存,连续将输出时钟锁存打开、关闭,以保证数据不会受到其他程序段的影响
//当三个数码管的数据都已经输入完成,那么把数据锁存起来
GPIO_SetBits(GPIOA,GPIO_Pin_2);//RCLK_H;
GPIO_ResetBits(GPIOA,GPIO_Pin_2);//RCLK_L;
}
二、BUTTON(ADC)
- 需要盖帽PA5—AKEY上,此时,按扩展板上的8个按钮,会输出相应的ADC值,ADC同样是12位的
- 初始化:GPIO模式:GPIO_Mode_AIN(模拟输入)、
ADC(ADC1_IN5,查看芯片手册,可以发现,PA5对应的是ADC1的通道5)
NVIC:配置优先级
时钟:GPIOA、ADC1 - 其他函数调用
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC时钟(只有这一个需要去"stm32f10x_rcc.h"里面找) ADC_Cmd(ADC1,ENABLE);//打开ADC ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); //中断配置 ADC_ResetCalibration(ADC1);//校准ADC ADC_StartCalibration(ADC1);//校准ADC while(ADC_GetCalibrationStatus(ADC1));//等待ADC校准完毕 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发 ADC_RegularChannelConfig(ADC1,ADC_Channel_5,1,ADC_SampleTime_13Cycles5);//规则通道配置
- 判断按下的是哪一个键
u8 Scan_Btn(void) { u16 btn_tmp = 0; //btn_tmp 通过中断方式读取ADC的值 //S1~S8按键按下后会分别产生不同的电阻分压值,通过ADC显示,其电压值大致分为14份,按下不同的键,会产生不同的电压值,每次读取到的值不一定完全相同,但一定在对应的区间当中 if(btn_tmp < 0x0FFF/14){ return 1;//按下按键S1 }else if((btn_tmp > 0x0FFF/14) && (btn_tmp < 0x0FFF/14*3)){ return 2;//按下按键S2 }else if((btn_tmp > 0x0FFF/14*3) && (btn_tmp < 0x0FFF/14*5)){ return 3;//按下按键S3 }else if((btn_tmp > 0x0FFF/14*5) && (btn_tmp < 0x0FFF/14*7)){ return 4;//按下按键S4 }else if((btn_tmp > 0x0FFF/14*7) && (btn_tmp < 0x0FFF/14*9)){ return 5;//按下按键S5 }else if((btn_tmp > 0x0FFF/14*9) && (btn_tmp < 0x0FFF/14*11)){ return 6;//按下按键S6 }else if((btn_tmp > 0x0FFF/14*11) && (btn_tmp < 0x0FFF/14*13)){ return 7;//按下按键S7 }else if((btn_tmp > 0x0FFF/14*13) && (btn_tmp < 0x0FDF)){ return 8;//按下按键S8 } else{ return 0; //error status & no key } }
三、温度传感器(DS18B20)
- 盖帽接PA6—TDQ
- 直接使用官方提供的ds18b20.c和ds18b20.h的初始化函数ds18b20_init_x();
并自己写一个函数,用来读取温度的值 - 读取温度值
s16 ds18b20_read(void)//返回值为16位数据 { u8 val1,val2; s16 x = 0; ow_reset(); ow_byte_wr(OW_SKIP_ROM); ow_byte_wr(DS18B20_CONVERT); delay_us(750000); ow_reset(); ow_byte_wr( OW_SKIP_ROM ); ow_byte_wr ( DS18B20_READ ); val1 = ow_byte_rd();//先读取低8位的温度值 val2 = ow_byte_rd();//再读取高8位的温度值 x = (val2<<8)+val1;//整合成16位的温度值 return x; }
四.温湿度传感器(DHT11)
- 盖帽接PA7—HDQ
- 直接使用官方提供的dht11.c和dht11.h的初始化函数dht11_init();
- 直接使用温湿度读取函数dht11_read();
unsigned int dht11_read(void);
//返回一个32位的数据,数据格式为:
//8位湿度整数+8位湿度小数+8位温度整数+8位温度小数
int DHT=dht11_read();
DHT>>24;//湿度的整数部分
(DHT>>8)&0xff//温度的整数部分
//因此在显示湿度时,应该右移24位,温度应该右移8位,同时忽略前16位
五、MEMS传感器(LIS302DL)
- 盖帽需要连接PA4—SCL、PA5—SDA
- MEMS传感器其实和EEPROM类似,都是使用I²C,只不过EEPROM使用引脚PB7、PB6,而MEMS传感器使用引脚PA5、PA4
- 同样的,直接使用官方提供的i2c.c、i2c.h文档,但要把
如果要同时使用EEPROM,则把这俩个文档名字改一下即可#define I2C_PORT GPIOB #define SDA_Pin GPIO_Pin_7 #define SCL_Pin GPIO_Pin_6 改为 #define I2C_PORT GPIOA #define SDA_Pin GPIO_Pin_5 #define SCL_Pin GPIO_Pin_4
-
写入LIS302DL、读取LIS302DL的函数
uint8_t LIS302DL_Read(uint8_t adds) { uint8_t data; I2CStart(); I2CSendByte(0x38); I2CWaitAck(); I2CSendByte(adds); I2CWaitAck(); I2CStart(); I2CSendByte(0x39); I2CWaitAck(); data=I2CReceiveByte(); I2CWaitAck(); I2CStop(); return data; } void LIS302DL_Write(uint8_t adds,uint8_t data) { I2CStart(); I2CSendByte(0x38); I2CWaitAck(); I2CSendByte(adds); I2CWaitAck(); I2CSendByte(data); I2CWaitAck(); I2CStop(); } //可以发现,LIS302DL的接受和发送函数和EEPROM的完全相同,区别只是器件的读写地址发生了变化。 //将0xa0、0xa1的读写EEPROM地址改为0x38、0x39的读写LIS302DL地址
六、光敏电阻(DO)
- DO数字量输出接口(0和1)
- 盖帽PA3—TRDO
- 初始化:配置GPIO:PA3——GPIO_Mode_IPU(上拉输入)
时钟:GPIOA - 模块在无光环境以及环境光线亮度达不到设定阈值时,DO端输出高电平,当外界环境光线亮度超过设定阈值时,DO端输出低电平;
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) == Bit_RESET){//GPIO_ReadInputDataBit:读取引脚输入电平
LCD_DisplayStringLine(Line7, (u8*)" DO:High ");//低电平表示亮度达到阙值
}else{
LCD_DisplayStringLine(Line7, (u8*)" DO:Low ");
七、光敏电阻(AO)
- DO模拟电压输出接口
- 盖帽PA4—TRAO
- 初始化:配置GPIO:PA4——GPIO_Mode_AIN(模拟输入)
ADC(ADC1_IN4,查看芯片手册,可以发现,PA4对应的是ADC1的通道4)
时钟:GPIOA、ADC1
若使用中断获取光敏电阻的值,那么配置NVIC:ADC_IT_EOC - 其他函数调用
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC时钟(只有这一个需要去"stm32f10x_rcc.h"里面找) ADC_Cmd(ADC1,ENABLE);//打开ADC ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); //中断配置 ADC_ResetCalibration(ADC1);//校准ADC ADC_StartCalibration(ADC1);//校准ADC while(ADC_GetCalibrationStatus(ADC1));//等待ADC校准完毕 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发 ADC_RegularChannelConfig(ADC1,ADC_Channel_5,1,ADC_SampleTime_13Cycles5);//规则通道配置
- 光敏电阻根据外界光强不同,会产生不同的电阻值,光照愈强,阻值就愈低。随着光照强度的升高,电阻值迅速降低,可降低至1KΩ以下。随着电阻下降,电压不断升高。ADC读取光敏电阻两侧的电压值,来量化光强度值
公式:tmp/(4096.-tmp)*10),其中tmp为ADC读取得到的值
八、AD采集x2
-
盖帽PA4—AO1,PA5—AO2
-
俩个ADC对应电位器RP5、RP6
-
初始化:GPIO模式:PA4、PA5 :GPIO_Mode_AIN(模拟输入)、
ADC(ADC1_IN4、ADC1_IN5,查看芯片手册,可以发现,PA4对应的是ADC1的通道4;PA5对应的是ADC1的通道5)
时钟:GPIOA、ADC1ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;//因为是同时使用俩个ADC,不能在配置为独立模式; ADC_InitStructure.ADC_NbrOfChannel = 2;//使用规则通道的数量为2个 //RegSimult:同步规则模式
NVIC:配置优先级
时钟:GPIOA、ADC1 -
其他函数调用
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC时钟(只有这一个需要去"stm32f10x_rcc.h"里面找) ADC_Cmd(ADC1,ENABLE);//打开ADC ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); //中断配置 ADC_ResetCalibration(ADC1);//校准ADC ADC_StartCalibration(ADC1);//校准ADC while(ADC_GetCalibrationStatus(ADC1));//等待ADC校准完毕 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发 ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5);//规则通道配置
-
读取ADC值
u16 Get_ADCs(u8 channel)//可以使用中断方式
{
//因为ADC1同时使用俩个通道读取,因此,每次读取ADC的值,都要选择要读取的通道
u16 ADC_Val = 0;
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5); //这一句是重点
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
ADC_Val = ADC_GetConversionValue(ADC1);
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
ADC_SoftwareStartConvCmd(ADC1, DISABLE);
return ADC_Val;
}
九、9.脉冲测量(PWM)
- 盖帽PA6—PWM1—电位器PR1—TIM3_CH1 ,PA7—PWM2—电位器PR2—TIM3_CH2
在这里插入代码片
- 初始化:GPIO模式:PA4、PA5 :GPIO_Mode_IN_FLOATING(浮空输入)、
TIM:配置结构体TIM_ICInitTypeDef,测量的是开发板上输入PWM,因此配置的是TIM输入结构体
时钟:GPIOA、TIM3
NVIC
其他函数调用
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI2FP2);//选择输入触发源:TI2:表示通道2,PF2:经过滤波器后将接到捕捉比较通道IC2;
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//选择从模式 上升沿重新初始化计数器,并且产生一个更新寄存器的信号
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);//配置从模式
TIM_Cmd(TIM3, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
中断函数
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET){
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
IC2Value = TIM_GetCapture2(TIM3);
if (IC2Value != 0){
DutyCycle = (TIM_GetCapture1(TIM3) * 100) / IC2Value;//占空比,
Frequency = SystemCoreClock / IC2Value;//频率,单位:HZ
}else{
DutyCycle = 0;
Frequency = 0;
}
}
}