目录
1.1 传感器的选择
1.2 传感单元的设计
1.3 原理介绍&代码浅析
1.3.1 ADC简介
1.3.2 ADC的触发
1.3.3 STM32的ADC详细原理
1.3.4 ADC的转换模式
1.3.5 DMA简介
1.3.6 代码浅析
整个跟踪系统中,传感器的选择和设计是最重要的部分,传感器部分选择设计的好坏直接影响跟踪器的跟踪效果。
对于传感器的选择,可以使用光敏器件将光信号转化为电信号供控制器分析处理;也可以直接使用图像传感器对太阳拍摄来分析太阳的实时位置。由于太阳移动缓慢且不会出现短时间内大幅度光强变化情况,考虑到价格因素,综合分析后决定采用光敏电阻作为感光元件进行设计。
光敏电阻是用硫化镉或硒化镉等半导体材料制成的特殊电阻器,其工作原理是基于内光电效应。光照愈强,阻值就愈低,随着光照强度的升高,电阻值迅速降低(即光照越强,ADC所采集的数值越低)。亮电阻值可小至1KΩ以下。光敏电阻对光线十分敏感,其在无光照时,呈高阻状态,暗电阻一般可达1.5MΩ。光敏电阻的特殊性能,随着科技的发展将得到极其广泛应用。本装置采用的是GL5516型号的光敏电阻,如图1所示。
由于单晶硅柔性太阳能板的特性,在本系统的设计中,传感单元垂直于太阳光的方向可允许有±5°以内的误差,根据此特点设计传感单元。
物理结构设计:拟使用四个型号相同的光敏电阻作为基本组件,按矩形的形状进行分布排列。将光伏板与传感器固定到同一个平面,使用时,比较四个光敏电阻接收到的光强信号,哪个信号强就控制电机往该方向转动。
电路设计:将光敏电阻的正极接入电源正极,与10K电阻串联后再与GND相连。在光敏电阻和保护电阻间引出模拟输入IO口,以供单片机进行ADC采集。在该装置中,光敏电阻用来实时检测太阳的光照强度,串联的电阻起到了保护和分压的功能。
将以上的物理结构设计和电路设计相结合,即为本系统所采用的光电传感单元——基于挡板的四象限光敏电阻探测器,如图2所示。
ADC(Analog-Digital Converter)模拟-数字转换器,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。通过ADC可以对高电平和低电平之间的任意电压进行量化,最终用一个变量来表示。更通俗来说,ADC其实就是一个电压表,把引脚的电压值测出来,放在一个变量里。
12位逐次逼近型ADC,1us转换时间。(转换时间一般不需要处理,一般AD转换都很快,如果不需要非常高速的转换频率,那转换时间就可以忽略)
输入电压范围:0~3.3V,转换结果范围:0~4095(2的12次方-1)。
18个输入通道,可测量16个外部和2个内部信号源。
规则组和注入组两个转换单元。
普通的AD转换流程是,启动一次转换、读一次值,以此往复循环;STM32则可以列一个组,一次性启动一个组,连续转换多个值。并且有两个组,一个是用于常规使用的规则组,一个是用于突发事件的注入组。
规则组虽然可以同时转换16个通道,但数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要尽快把结果拿走。
注入组一次性最多可以转换4个通道,并且其数据寄存器有4个,是可以同时存储4个数据的,不用担心被覆盖的问题。
一般情况下,使用规则组就完全足够了。但在使用规则组时,往往配合DMA进行使用,这样就不需要担心数据被覆盖的问题了。
模拟看门狗自动监测输入电压范围。
可以设定好上阈值和下阈值,达到即申请中断,可免去手动if来读值判断。
顺便对DAC的一些补充:
有ADC自然也有DAC,目前DAC应用最广泛熟知的为PWM,PWM即为DAC的功能(将数字变量转化为模拟电压)。同时,PWM只有完全导通和完全断开两种状态,在这两种状态上都没有功率损耗,因此在直流电机调速这种大功率的应用场景,使用PWM等效模拟量,是比DAC更好的选择,且PWM电路更加简单、常用。目前DAC的应用主要是在波形生成这些领域,比如信号发生器、音频解码芯片等。
对于STM32的ADC,触发ADC开始转换的信号有两种,一种是软件触发,即在程序里手动调用一条代码;另一种是硬件触发,即特定的触发源,这些触发源主要来自定时器,有定时器的各个通道,还有TRGO定时器主模式的输出。把TIM3定时为1ms,并且把TIM3的更新事件选择为TRGO输出,然后在ADC这里,选择开始触发信号为TIM3的TRGO,这样TIM3的更新事件就能通过硬件自动触发ADC转换了。
ADCCLK<最大14MHz,所以ADC预分频器只能选择6分频(12M)或者8分频(9M)。
要弄清STM32的ADC,具体看江协科技的STM32教程中 P21 [7-1]ADC模数转换器https://www.bilibili.com/video/BV1th411z7sn?p=21&vd_source=b4d125df2ebf1ab26fbed06ba725ac39的讲解。讲得太清晰太棒了!!
ADC的输入通道:
单次转换,非扫描模式
触发一次,转换一次,转换完成后产生标志位
需要再次触发才能再次进行转换
连续转换,非扫描模式
触发一次,开始转换,每次转换完成后不需再次触发即可再次进行转换
转换完成一次产生一次标志位,若需要读取数据,直接在数据寄存器里读取即可
单次转换,扫描模式
触发一次,完成一系列通道的转换后才产生标志位。(初始化时,还应设置 通道数目)
需要再次触发才能再次进行一轮转换
扫描模式下,为防止数据被覆盖,就需要用DMA及时将数据挪走
连续转换,扫描模式
即在“单次转换,扫描模式”模式下变为 每次转换完成后不需再次触发即可再次进行转换,同样的,为防止数据被覆盖,就需要用DMA及时将数据挪走。
在扫描模式下,还有一个“间断模式”,作用是,在扫描过程中,每隔几个转换,就暂停一次,需要再次触发才能继续。
数据对齐
由于寄存器是16位的,而ADC所采集的数据最大为12位的,因此需要进行数据对齐。
一般用右对齐,因为左对齐相当于左移4位,二进制中左移4位相当于×2,得到的数据会比实际大16倍。
DMA(Direct Memory Access)直接存储器存取,它主要是用来协助CPU,完成数据转运的工作。
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。
12个独立可配置的通道:DMA1(7个通道),DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发。
更多对STM32的DMA的详细讲解,移步到江协科技的STM32教程中 P23 [8-1]DMA直接存储器存取https://www.bilibili.com/video/BV1th411z7sn?p=23&vd_source=b4d125df2ebf1ab26fbed06ba725ac39
本系统采用了ADC连续扫描模式+DMA来实现对4个光敏电阻进行实时的数据采集。
每一句代码后面都有注释解释清楚这一步是干嘛的。
uint16_t AD_Value[4];
void My_ADC_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF | RCC_APB2Periph_ADC3 , ENABLE ); //使能GPIOF时钟和ADC3通道时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); //使能 DMA2 时钟
//用于配置ADCCLK分频器
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_7; //PF7、8、9、10 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入 模式
GPIO_Init(GPIOF, &GPIO_InitStructure);
ADC_DeInit(ADC3); //复位ADC3,将外设 ADC3 的全部寄存器重设为缺省值
DMA_DeInit(DMA2_Channel5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5 ); //选择规则组的输入通道
ADC_RegularChannelConfig(ADC3, ADC_Channel_6, 2, ADC_SampleTime_55Cycles5 );
ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 3, ADC_SampleTime_55Cycles5 );
ADC_RegularChannelConfig(ADC3, ADC_Channel_8, 4, ADC_SampleTime_55Cycles5 );
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC3工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //模数转换工作在扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 4; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC3, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADC3的寄存器
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR; //DMA 外设 ADC 基地址,为ADC_DR的地址0x4001244C
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //DMA 内存基地址,可理解为转存的目的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向:外设到内存,即外设站点是源
DMA_InitStructure.DMA_BufferSize = 4; //传输数量,因为4个ADC通道,所以传输4次
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变(不自增),始终转运同一个位置的数据
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为 16 位,半字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为 16 位,半字
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DM 通道拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输,不使用软件触发;这里需要硬件触发,触发源为ADC3。
DMA_Init(DMA2_Channel5, &DMA_InitStructure); //初始化 DMA 的通道,这里若使用硬件触发,则需要注意所使用的硬件源对应哪个通道
DMA_Cmd(DMA2_Channel5,ENABLE); //使能指定的DMA2_Channel5
ADC_DMACmd(ADC3,ENABLE); //使能ADC到DMA的输出
ADC_Cmd(ADC3, ENABLE); //使能指定的ADC3
ADC_ResetCalibration(ADC3); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC3)); //等待复位校准结束
ADC_StartCalibration(ADC3); //开启AD校准
while(ADC_GetCalibrationStatus(ADC3)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC3, ENABLE); //使能指定的ADC3的软件转换启动功能(连续转换模式只需触发一次即可)
}
整个过程仅需完成初始化,不需要程序手动进行操作,即可随时读取最新的ADC所采集的数据,节省软件资源,实现硬件自动化,这是STM32的一大特色之一。外设之间互相连接,互相合作,在完成简单且繁琐的工作时,不需要CPU统一调度,外设之间就会相互配合自动完成这些繁琐的工作。