一、硬件准备
- STM32 开发板。
- 光照传感器(通常是光敏电阻或光电二极管等),并连接到 STM32 开发板的 ADC 输入引脚。
二、软件实现
-
配置开发环境:
- 安装 STM32 的开发工具,如 Keil MDK 等。
- 创建一个新的工程。
-
配置 ADC:
- 开启 ADC 时钟。在 STM32 中,不同型号的芯片开启时钟的方式可能略有不同,但通常可以通过 RCC(Reset and Clock Control)寄存器来实现。
- 配置 ADC 的采样时间、分辨率等参数。可以根据实际需求选择合适的采样时间和分辨率。例如,如果需要更高的精度,可以选择较长的采样时间和较高的分辨率。
- 选择 ADC 的输入通道,连接到光照传感器的引脚。
-
配置串口:
- 开启串口时钟。
- 配置串口的波特率、数据位、停止位等参数。
- 使能串口发送和接收功能。
-
读取 ADC 值并转换为光照程度:
- 启动 ADC 转换。可以通过设置相应的寄存器来启动 ADC 转换。
- 等待 ADC 转换完成。可以通过查询标志位或使用中断的方式来等待转换完成。
- 读取 ADC 的转换结果。转换结果通常是一个数字值,表示输入电压的大小。
- 根据光照传感器的特性,将 ADC 值转换为光照程度。这通常需要通过实验或查阅传感器的数据手册来确定转换公式。例如,如果光照传感器的输出电压与光照程度成线性关系,可以使用线性插值的方法进行转换。
-
通过串口输出光照程度:
- 将光照程度转换为字符串格式。可以使用 sprintf 等函数将数字转换为字符串。
- 使用串口发送函数将字符串发送出去。可以使用 USART_SendData 等函数将字符串逐个字符发送出去。
例子:基于STM32F405做一个光照收集并在串口一输出的
(关于初始化的,可以看芯片手册,中文手册,权威指南)
不知道该配置的一定要多看这些资料,以及原理图(有查不到的可以私信,我帮找找看)
adc.h 中的ADC初始化:
#include "adc.h"
#include "stm32f4xx.h" // Device header
void adc_1_init(void)
{
// 使能 GPIOC 的时钟(AHB1 总线)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
// 使能 ADC1 的时钟(APB2 总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 设置 GPIO 模式为模拟模式,用于连接光敏传感器
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
// 选择引脚 PC0
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
// 不使用上拉或下拉电阻
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
// 初始化 GPIOC 的引脚配置
GPIO_Init(GPIOC, &GPIO_InitStruct);
ADC_InitTypeDef ADC_InitStruct = {0};
// 禁用连续转换模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
// 设置数据对齐方式为右对齐
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
// 设置转换序列长度为 1,即只进行一次转换
ADC_InitStruct.ADC_NbrOfConversion = 1;
// 设置 ADC 分辨率为 12 位
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
// 禁用扫描模式,只对一个通道进行转换
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
// 初始化 ADC1 的参数
ADC_Init(ADC1, &ADC_InitStruct);
ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
// 设置 ADC 预分频器为 4,调整 ADC 时钟频率
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4;
// 设置两个采样之间的延迟为 5 个 ADC 时钟周期
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
// 初始化 ADC 的通用参数
ADC_CommonInit(&ADC_CommonInitStruct);
// 以下是中断相关配置,这里注释掉了,暂时不需要中断功能,开了中断反而有点输出不顺畅
// NVIC_InitTypeDef NVIC_InitStruct = {0};
// 设置中断通道为 18,对应 ADC1 的中断
// NVIC_InitStruct.NVIC_IRQChannel = 18;
// 使能中断通道
// NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
// 设置中断优先级为 2,子优先级为 2
// NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
// NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
// 初始化中断控制器
// NVIC_Init(&NVIC_InitStruct);
// 使能 ADC1,准备进行转换
ADC_Cmd(ADC1, ENABLE);
}
adc.h中的获取数据的服务函数:
int adc_1_GetFlagStatus(void)
{
int i = 0;
float a[10];
float temp;
// 进行 10 次采样
for (i = 0; i < 10; i++)
{
// 配置 ADC1 的规则通道为通道 10,采样顺序为 1,采样时间为 480 个 ADC 时钟周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_480Cycles);
// 软件启动 ADC1 的转换
ADC_SoftwareStartConv(ADC1);
// 等待转换结束标志位 ADC_FLAG_EOC 被置位,表示转换完成
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {}
// 将转换结果存储到数组 a 中
a[i] = ADC_GetConversionValue(ADC1);
}
// 对采样结果进行冒泡排序
for (int i = 0; i < 10 - 1; i++)
{
for (int j = 0; j < 10 - i - 1; j++)
{
if (a[j] > a[j + 1])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
// 返回排序后的中间两个值的平均值
return (a[4] + a[5]) / 2;
}
对于这个冒泡排序是希望数据不要出现偶然性,比如,突然出现一个很大的数据反而影响我们想要的数据,当然,这个冒泡取中值我感觉也不算完美,期待大家改进。
串口一的初始化以及服务函数:
#include "usart1.h"
void usart1_init(u32 baud)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//初始化配置
GPIO_InitTypeDef GPIO_InitStruct={0};
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//通用输入模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 |GPIO_Pin_10;//引脚
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;//速度
GPIO_Init( GPIOA, & GPIO_InitStruct);//IO口初始化
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);//映射
USART_InitTypeDef USART_InitStruct ={0};
//波特率
USART_InitStruct.USART_BaudRate =baud;//最高不能超过2M
USART_InitStruct.USART_Mode =USART_Mode_Rx |USART_Mode_Tx;//接收和发送
//帧格式
USART_InitStruct.USART_Parity =USART_Parity_No ;//无校验
USART_InitStruct.USART_StopBits =USART_StopBits_1;//1位 停止
USART_InitStruct.USART_WordLength =USART_WordLength_8b;//数据8位
//硬件流控
//
//选择接收和空闲中断源
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
//选择中断和开中断
NVIC_InitTypeDef NVIC_InitStruct={0};
NVIC_InitStruct.NVIC_IRQChannel =37; //中断编号(向量表),主控头文件
NVIC_InitStruct.NVIC_IRQChannelCmd =ENABLE; //开中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2 ; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority =2;
NVIC_Init(& NVIC_InitStruct);
USART_Init(USART1, &USART_InitStruct);//USART口初始化
//开启串口一--USART1
USART_Cmd(USART1, ENABLE);
}
/*中断服务函数--函数名固定的--启动文件 .s*/
char sp[256]={0};
int USART1_Flag=0;//接收完成标记位
int USART1_XB=0;//数组下标
void USART1_IRQHandler(void)
{
//判断是接收中断-USART
if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET)
{
sp[USART1_XB++]= USART_ReceiveData(USART1);//读取数据,并清接收标记位
if(USART1_XB>255) USART1_XB=0;//数组下标不能超过最值
}
//判断是空闲中断-USART
else if(USART_GetITStatus(USART1, USART_IT_IDLE)==SET)
{
sp[USART1_XB]='\0';//转成字符串数组
USART1_Flag=1;//标记接收完成
USART_ReceiveData(USART1);//清空闲中断标记
}
}
/*发送接收*/
void usart1_send(char sp)//发送一个字符函数
{
//等待上次发送完成
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//TC
USART_SendData(USART1, sp);
}
void usart1_send_str(char *sp)//芯片发送一个指针,达到连续输入字符效果
{
while(*sp!='\0')//循环
usart1_send(*sp++);
}
//接收一个字符函数
char usart1_receive(void)
{
//等待接收完成完成--USART_GetFlagStatus
//return USART_ReceiveData()
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
return USART_ReceiveData(USART1);
}
void usart1_receive_str(char *sp)//接收电脑发送的字符串并存入到字符指针
{
int i=0;
while(1)
{
sp[i]=usart1_receive();//接收一个字符
if(sp[i]=='\r'||sp[i]=='\n')//判断是否接收完毕,串口发送完毕会最后发送回车和换行
{
sp[i]='\0';//跳出上面芯片发送的循环
return ;//跳出当前循环
}
i++;//循环接收
if(i>255) i=0;
}
}
主函数:
int main()
{
systick_init(168);//系统滴答初始化,我的延迟函数在里面
usart1_init(115200);//115200是波特率
adc_1_init();
while(1)
{
printf("光照强度:%.2f%%\r\n",(1-adc_1_GetFlagStatus()/4095.0)*100); //获取的数据转化
//成百分比
delay_ms(200);
}
}
串口收集到的数据:
到此就结束咯,如果有还觉得不过瘾的友友,还可以拿温度传感器或者火焰传感器来练练手,打印输出收集到的数据!操作都差不多的!
今天就到这啦!文中若有哪些有误的,恳请友友们能在评论区指出!感激不尽!