STM32学习日记:探索光敏传感器模块

文章目录

  • 前言
  • 一、光敏传感器引脚
  • 二、光敏传感器小实验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();
    	}
    	else
    		LED_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  PA9
    	MyUsart_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  PA10
    	MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
    	MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    	GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);
    	
    	
    	// 初始化串口2
    	MyUSART_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 );
    	
    
    }
    
    
    //重定向 printf
    int 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 header
    
    
    void 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);
    	
    	// 初始化ADC1
    	ADC_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);//校验ADC
    	
    	while(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 <stdio.h>
    
    #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)在我大哥的指点下,用了他的思路:先把浮点型数据转换为整型的,然后直接传这个整型的数据,再一位一位的显示(就是取最高位到最低位,挨着取出来显示,调用显示一个字符的函数。)。

    欧克,结束了,学到很多。。。。加油!!

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32学习日记:探索光敏传感器模块

    发表评论