文章目录
- 前言
- 一、光敏传感器引脚
- 二、光敏传感器小实验1
- 1·读取DO引脚的高低电平来控制一个led的亮灭。
- 2·这里提一下GPIO的工作模式
- 3·这里提一下GPIO的输出速度
- 三、光敏传感器小实验2
- 1·光敏传感器DO的高低电平变化来切换OLED的显示。
- 四、光敏传感器小实验3
- 1·串口打印出 .光敏AO模拟输出 .经过32的ADC1 通道0 显示光强数据。
- 五、光敏传感器小实验4
- 1· 在小实验4的基础上,用OLED显示ADC采集的数据
前言
这是我学习32的心理路程
我会尽量把内容写的仔细
对于一些简单模块的使用,我不打算深究其原理,我只要会用它就欧克。所以看见我的32博客的同志们请注意这一点。如果同志您觉得内容有改进的地方,那就请多多指教啦~~~本人一定认真听取。
一、光敏传感器引脚
我这里使用的光敏传感器是4针的。
AO | 模拟输出:光敏传感器将采集的光线变成一个连续的模拟信号从AO引脚输出 |
---|---|
DO | 数字输出:大于光线阈值,DO引脚输出1(高电平);反之输出0。关于光线阈值,应该是调节模块上面那个十字架旋钮 |
GND | 接地 |
VCC | 3·3v或者5v |
注意:这是我第一次使用这个模块,所以语言描述可能有bug。
3针的光敏传感器就好像没有DO引脚。其他的是一样的。
二、光敏传感器小实验1
1·读取DO引脚的高低电平来控制一个led的亮灭。
这个小实验很简单哈。
lightsensor.c
#include "stm32f10x.h" // Device header#define LED_OFF() GPIO_SetBits(GPIOB, GPIO_Pin_1 )#define LED_NO() GPIO_ResetBits(GPIOB, GPIO_Pin_1 )/** * @brief 光敏传感器DO引脚所连接32的PC14引脚的初始化, PC14设置为输入模式,读取DO传过来的电平。 * @param 无 * @retval 无 */void Light_Sensor_GPIOinit(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitTypeDef Light_Sensor_GPIO_InitStruct;Light_Sensor_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU ;Light_Sensor_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;Light_Sensor_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC,&Light_Sensor_GPIO_InitStruct);}/** * @brief 光敏传感器所控制的led的初始化,这里我是自己外接的led * @param 无 * @retval 无 */void Test_Sensor_LEDinit(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef Test_Sensor_LEDinit;Test_Sensor_LEDinit.GPIO_Mode = GPIO_Mode_Out_PP ;Test_Sensor_LEDinit.GPIO_Pin = GPIO_Pin_1;Test_Sensor_LEDinit.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&Test_Sensor_LEDinit);LED_OFF(); //high--->led off}/** * @brief 功能函数,读取DO电平,来控制外接LED * @param * @retval */void Sensor_Contral_LED(void){//uint8_t value = 0;if( GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_14) == 0 ){LED_OFF();}elseLED_NO(); }
main.c
#include "stm32f10x.h" // Device header#include "lightsensor.h"int main (void){Light_Sensor_GPIOinit();Test_Sensor_LEDinit();while(1){//GPIO_ResetBits(GPIOB, GPIO_Pin_1 );// 测试外接led单独能不能亮 Sensor_Contral_LED();}}
注意:lightsensor.h就是将lightsensor.c中的函数名复制一下就欧克了,这里就不多展示 了哈!
对于这个实验的总结:
开始测试外接led能不能单独亮的时候,led死活不亮,我还以为是引脚复用的毛病,但是我差了一下资料发现并不是这个问题,结果回去仔细看了一下引脚初始化才发现led要推挽输出(),以及引脚初始化结构体的问题。我麻了。后来修正后就成功点亮了,虽然这个问题是小小的不起眼的问题,至少还是需要总结一下,避免以后再犯~~~
2·这里提一下GPIO的工作模式
4种输出模式:
(1)推挽输出:使用推挽输出目的是增大电流,即提高输出引脚的驱动能力,提高电路负载能力。【驱动led】
(2)开漏输出:由于只有下拉MOS管没有上拉MOS管,所以开漏输出模式下,IO引脚只能输出低电平。如果要输出高电平,则需要外接上拉电阻。
(3)复用推挽,复用开漏输出:IO引脚做复用功能时,可以选则复用推挽或者复用开漏输出模式,在选择复用开漏输出模式时,需要外接上拉电阻。
4种输入模式:
(1)上拉输入模式:引脚内部有一个上拉电阻,通过开关连接到电源VDD,当IO引脚无输入信号时,默认输入高电平。【外接按键】
(2)下拉输入模式:与上拉输入模式相反。当IO引脚无输入信号时,默认输入低电平。
(3)浮空输入:引脚内部即不接上拉也不接下拉电阻。浮空输入模式下的引脚电平是不确定的,外部信号是上面电平,MCU引脚就输入什么电平。【USART,IIC等通信协议】
(4)模拟输入模式:引脚内部即不接上拉也不接下拉电阻。【A/D模拟输入实现对外部信号的采集】
3·这里提一下GPIO的输出速度
(1)输出速度并不是输出信号的的速度,而是IO口驱动电路的响应速度。“我简称为:反应能力”。
(2)32 的输出速度有3种: 2MHz, 10MHz, 50MHz,对应的就是低速反应能力,中速反应能力,高速反应能力。
当输出配置为高速时,噪声大,功耗高,电磁干扰强;当输出为低速时,噪声小,功耗低,电磁干扰弱。当输出较高频率的信号时,应该选用较高频率响应速度的驱动模块,否则容易出现信号失真现象。
一般常用的外设(例如led,蜂鸣器等)建议采用2MHz的输出速度,而作为IIC,SPI等复用功能的输出引脚时,尽量选择高响应速度,如10mhz,50hmz。
GPIO引脚做输入模式时,不需要配置引脚的输出速度。
三、光敏传感器小实验2
稍微提升一下小实验1的难度。
1·光敏传感器DO的高低电平变化来切换OLED的显示。
这里我就不贴OLED的代码了哈。
这个实验就是在实验1的lightsensor.c中加一个函数就行。
void SensorContral_LED_OLED(void){//uint8_t value = 0;if( GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_14) == 0 ){LED_OFF();OLED_Clear();OLED_ShowString(2,2,"light hight");}else{LED_NO(); OLED_Clear();OLED_ShowString(2,2,"light low");}}
main.c
int main (void){Light_Sensor_GPIOinit();Test_Sensor_LEDinit();OLED_Init();while(1){SensorContral_LED_OLED();}}
实验总结:这个实验和实验1其实本质上上没有什么大差别,只是将led换成了OLED而已,我就想着慢慢来使用OLED屏幕,后面会慢慢提升到32用ADC采样采取光敏传感器的AO的模拟输出信号,用OLED来显示更加精确的亮度值。
对于实验2 ,我遇到一个问题:OLED出现闪屏现象。我猜测应该是主函数中的while(1)在一直循环SensorContral_LED_OLED这个函数,这个函数内部对OLED进行了清屏操作,所以就出现了闪屏现象。现在正在解决,若是解决了就放修正后代码。
修正代码的思路:(1)由于我们OLED 是静态显示,所以就不需要清屏刷新.因为在光敏传感器里面如果调用OLED的清屏函数,加上主函数while循环里面调用这个功能函数,就相当于while循环会一直有OLED清屏,所以会出现闪屏现象。(2)light hight 与 light low 长度不一样,在显示的时候会出现light lowht , 所以在light low 后面补充两个空格符,方便静态显示的时候不会出现交叠。
这刚好是OLED的静态显示所以不需要清屏刷新,但是遇到动态实时显示,需要清屏刷新怎么办捏????不需要
四、光敏传感器小实验3
1·串口打印出 .光敏AO模拟输出 .经过32的ADC1 通道0 显示光强数据。
接线:光敏的AO接32的PA0(PA0是ADC1通道0,所以已经在adc模块里面初始化了,不需要再初始化)
.
上面函数:获取串口状态标志。其中第二个传参就是下面这张图。
无法打开串口的情况:串口调试助手没有关闭,导致串口被占用,烧录不了。
STM32F103C8T6 的引脚资源表。
myusart.c
#include "myusart.h"void MyUsart_init(void){GPIO_InitTypeDef MyUsart_GPIO_InitStruct ;USART_InitTypeDef MyUSART_InitStruct; // 这两个结构体的定义一定要放在时钟的之前,否者编译器警告RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 ,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA,ENABLE);// usart1 Tx PA9MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP ;MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;MyUsart_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);//usart1 Rx PA10MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);// 初始化串口2MyUSART_InitStruct.USART_BaudRate = 115200 ;// 波特率MyUSART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制MyUSART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 收和发模式MyUSART_InitStruct.USART_Parity = USART_Parity_No;// 无奇偶校验位MyUSART_InitStruct.USART_StopBits = USART_StopBits_1;// 一位停止位MyUSART_InitStruct.USART_WordLength = USART_WordLength_8b;// 字长为8位的数据模式USART_Init(USART1, &MyUSART_InitStruct);USART_Cmd(USART1, ENABLE);// 使能串口2}// 发送一个字符void Usart_send_byte(USART_TypeDef* USARTx,uint16_t Data){USART_SendData(USARTx,Data);while( USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET );}// 发送字符串,遇到字符串结尾标志‘\0’结束void Usart_send_string(USART_TypeDef* USARTx,char *arr){uint16_t i = 0;do{Usart_send_byte(USARTx,*(arr + i));i++;}while(*(arr + i) != '\0');while( USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET );}//重定向 printfint fputc(int ch,FILE *f){USART_SendData(USART1,(uint8_t)ch);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);return (ch);}//重定向 输入 int fgetc(FILE *f){while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(USART1);}
adc.c
#include "stm32f10x.h" // Device headervoid ADC_init(void) {GPIO_InitTypeDef ADC_GPIO_InitStructure;ADC_InitTypeDef ADC_InitStruct;// 结构体的定义需要先于RCC的开启,否者报警告RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO ,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);// 由于ADC的最大时钟不超过14MHz,所以需要把时钟降下来。// ADC时钟分频器;六分频;时钟从APB2总线来,最大为72MHz,所以需要六分频后12MHz。RCC_ADCCLKConfig(RCC_PCLK2_Div6);// 使用的是 ADC1 初始化 PA0 用于采集光敏传感器的模拟输出ADC_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;// 模拟输入ADC_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 由于是输入模式,所以不需要配置Speed.GPIO_Init(GPIOA,&ADC_GPIO_InitStructure);// 初始化ADC1ADC_InitStruct.ADC_ScanConvMode = DISABLE;//扫描多通道还是单通道?单通道。ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 连续转换还是单次转换?连续。ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;// 数据对齐方式。右对齐。ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//定义用于启动模拟的外部触发器到常规频道的数字转换。ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//将ADC配置为独立还是多模?ADC_InitStruct.ADC_NbrOfChannel = 1;//通道数量ADC_Init(ADC1, &ADC_InitStruct);// ADC规则组配置ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);// ADC1,通道0,需要转换数是1,采样周期是55.5。ADC_Cmd(ADC1,ENABLE);ADC_ResetCalibration(ADC1);//对ADC进行复位while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC复位完成ADC_StartCalibration(ADC1);//校验ADCwhile(ADC_GetCalibrationStatus(ADC1));//等待ADC校验完成ADC_SoftwareStartConvCmd(ADC1,ENABLE);//开启ADC的软件触发}float ADC_GetValue(void){uint32_t Value = 0;float ret = 0.0;for(uint8_t i = 0;i < 30;i++){ADC_SoftwareStartConvCmd(ADC1,ENABLE);while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//转换结束标志, 转换完成该标志就清零Value += ADC_GetConversionValue(ADC1);}Value /= 30; // 这里求的是平均值ret = 30.303 * (3.3 - Value *(3.3/4095));// 整体上表示模数转换后的光度;4095表示12为的ADC的精度(不能变);// 3.3表示这个引脚最大承受电压return ret;}/*这里的 ret的运算就小小的运用了一下小算法,如果PA0不接光敏传感器,PA0引脚还是有电压的,(光敏传感器就是一个光敏电阻收到光线的影响来改变了电阻的阻值,从而改变了电压,所以知道PA0的电压就可以类比出此刻的光线的强度)我们以100作为光线最强的情况,0作为光线最弱的情况。PA0引脚所承受的最大电压是3.3V,所以我们需要将3.3V 与 100的光强进行一个类比。所以100 除以 3.3 等于 30.303。所以3.3V里面的一个单位1对应的就是100里面的30.303这也是为什么要乘以一个30.303的原因。*/
main.c
#include "stm32f10x.h" // Device header#include #include "lightsensor.h"#include "myusart.h"#include "Delay.h"#include "adc.h"int main (void){Light_Sensor_GPIOinit();MyUsart_init();ADC_init();while(1){ printf("ADC = %.3f \n",ADC_GetValue());Delay_ms(500);}}这里延时半秒是:避免串口调试助手疯狂答应导致卡死。
实验现象:
五、光敏传感器小实验4
1· 在小实验4的基础上,用OLED显示ADC采集的数据
因为ADC采集的数据会出现小数点,所以我需要一个可以显示浮点型数据的OLED功能函数。
代码如下:
OLED.c :部分代码
/** * @brief OLED显示一个字符 * @param Line 行位置,范围:1~4 * @param Column 列位置,范围:1~16 * @param Char 要显示的一个字符,范围:ASCII可见字符 * @retval 无 */void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char){ uint8_t i;OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);//设置光标位置在上半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i]);//显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);//设置光标位置在下半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);//显示下半部分内容}}//OLED 显示浮点型数据// Line : 第几行// Colum: 第几列// Number:需要显示的浮点数(需要把浮点数据扩大为它的整型)void OLED_FloatNum1(uint8_t Line, uint8_t Column, uint32_t Number){OLED_ShowChar(Line,Column + 0,(Number/10000) + '0');// 最高位OLED_ShowChar(Line,Column + 1,(Number/1000%10) + '0');OLED_ShowChar(Line,Column + 2,'.');// 固定显示OLED_ShowChar(Line,Column + 3,(Number/100%10) + '0');OLED_ShowChar(Line,Column + 4,(Number/10%10) + '0');OLED_ShowChar(Line,Column + 5,(Number%10) + '0');}
就是用这两个函数来显示浮点型数据。
ADC.c:采样功能代码,实验3这个部分和这里大致没有差别,这个代针对OLED,所以返回值会有一个小小的处理。
float ADC_GetValue(void){uint32_t Value = 0;float ret = 0.0;for(uint8_t i = 0;i < 30;i++){ADC_SoftwareStartConvCmd(ADC1,ENABLE);while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//转换结束标志, 转换完成该标志就清零Value += ADC_GetConversionValue(ADC1);}Value /= 30; // 这里求的是平均值ret = 30.303 * (3.3 - Value *(3.3/4095));//整体上表示模数转换后的光度;4095表示12为的ADC的精度;100就表示最大光度。return ret*1000;// 先把浮点型数据给扩大为它的整型,有利于OLED的功能函数处理显示这个数据}
总结:主要是OLED显示浮点型数据那一块费了一些些时间。
处理浮点型数据的思路:(1)刚开始我就是简单的以为把浮点型数据分为整数部分和小数部分,整数部分就经常调用显示十进制函数,小数部分就是先把小数扩大成整数,然后固定显示小数点,把扩大后的小数以整数的形式显示在小数点后面,事实证明,只能显示写死的浮点型数据,不能显示总是刷新的数据,小数部分显示不出来。
(2)在我大哥的指点下,用了他的思路:先把浮点型数据转换为整型的,然后直接传这个整型的数据,再一位一位的显示(就是取最高位到最低位,挨着取出来显示,调用显示一个字符的函数。)。
欧克,结束了,学到很多。。。。加油!!