上一篇 ->
原创不易,转载请注明出处,互相尊重劳动成果,谢谢!
三. MCU 软件设计构建
1. MCU核心温度采样
先实现基于片内ADC采样核心温度的功能,数据采样使用定时采样方式,采样数据基于DMA传输,减少CPU占用。
(1)设计框架
H743内部有三个独立的ADC转换器,除了能够采样连接到外部的模拟引脚外,还有三个内部的模拟采样通道,分别为核心温度、参考电压、VBat电压。
所使用的开发板上,有一个可调电阻连接到了PC3引脚,查DataSheet可知,该引脚可作为 ADC3 的 Channel 1 使用:
因此在建立片内采样驱动时,使用 ADC3 作为采样ADC,分别采样:
* 外部引脚PC3上的电压数值
* 内部参考电压
* 内部温度传感器
* VBAT电压
四路模拟信号数值。采样结果通过DMA传输保存到变量中。
采样使用定时100毫秒采样一次,既10Hz采样率。每次连续采样上述四个通道后,触发处理信号,通知检测任务,处理采样数据,并通过串口传输出去。
(2) 片内ADC初始化与驱动
片内 ADC3 的初始化,基于LL固件库接口,参考LL库中关于ADC的使用例程移植改造而来。初始化过程中,需要注意的是对于上述四个采样通道的设置。
目前仅在启动后的初始化中做一次通道的配置,之后运行不再动态改变,因此配置过程比较简单,基本是先配置通道,再启动ADC,之后运行过程中不再配置,因此不需要做配置时的复杂判断逻辑。而在运行过程中动态切换通道配置时,是需要先停止ADC,完全停止后再配置通道,之后再开启采样的。
通道配置中,采样模式设置为软件触发而不是连续自动触发,以满足10Hz的采样率控制;
调用 LL_ADC_REG_SetSequencerLength 接口,设置总采样通道数量为 4 ;
要使能三个内部采样通道,需要调用接口 LL_ADC_SetCommonPathInternalCh,允许对指定通道进行采样;
最后,定义四个采样通道的顺序。
通道配置代码如下:
if ((LL_ADC_IsEnabled(TEMP_ADC) == 0) ||
(LL_ADC_REG_IsConversionOngoing(TEMP_ADC) == 0) )
{
LL_ADC_REG_SetTriggerSource(TEMP_ADC, LL_ADC_REG_TRIG_SOFTWARE);
LL_ADC_REG_SetOverrun(TEMP_ADC, LL_ADC_REG_OVR_DATA_OVERWRITTEN);
LL_ADC_REG_SetSequencerLength(TEMP_ADC, LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS);
LL_ADC_SetCommonPathInternalCh(ADC3_COMMON, LL_ADC_PATH_INTERNAL_VREFINT | LL_ADC_PATH_INTERNAL_TEMPSENSOR | LL_ADC_PATH_INTERNAL_VBAT);
LL_ADC_REG_SetSequencerRanks(TEMP_ADC, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_VREFINT);
LL_ADC_REG_SetSequencerRanks(TEMP_ADC, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_TEMPSENSOR);
LL_ADC_REG_SetSequencerRanks(TEMP_ADC, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_VBAT);
LL_ADC_REG_SetSequencerRanks(TEMP_ADC, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_1);
}
这里,定义驱动所使用的 TEMP_ADC 为 ADC3:
#define TEMP_ADC ADC3
接下来定义各个通道的采样时间:
LL_ADC_SetChannelSamplingTime(TEMP_ADC, LL_ADC_CHANNEL_VREFINT, LL_ADC_SAMPLINGTIME_810CYCLES_5);
LL_ADC_SetChannelSamplingTime(TEMP_ADC, LL_ADC_CHANNEL_TEMPSENSOR, LL_ADC_SAMPLINGTIME_810CYCLES_5);
LL_ADC_SetChannelSamplingTime(TEMP_ADC, LL_ADC_CHANNEL_VBAT, LL_ADC_SAMPLINGTIME_810CYCLES_5);
LL_ADC_SetChannelSamplingTime(TEMP_ADC, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_810CYCLES_5);
因为需求为100毫秒完成一轮采样,所有各个通道的采样时间都设置为最大,以提管转换稳定性。
最后,在启动ADC前,需要先自校准一下,之后再启动。自校准需要一定的时间,具体的启动过程如下:
LL_ADC_DisableDeepPowerDown(TEMP_ADC);
LL_ADC_EnableInternalRegulator(TEMP_ADC);
tx_thread_sleep(2);
LL_ADC_StartCalibration(TEMP_ADC, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED);
while (LL_ADC_IsCalibrationOnGoing(TEMP_ADC) != 0)
{
tx_thread_sleep(2);
}
tx_thread_sleep(2);
LL_ADC_Enable(TEMP_ADC);
while (LL_ADC_IsActiveFlag_ADRDY(TEMP_ADC) == 0)
{
tx_thread_sleep(2);
}
(3)DMA配置
ADC转换完成后,使用DMA将转换结果传输到内存变量中。由于ADC配置为连续采样4个通道,因此每个通道采样转换完成后,会自动触发DMA动作,将结果传输到内存地址中,并开启下一个通道转换。当四个通道全部采样转换完成后,会触发DMA传输中断,可在此中断中添加处理转换完成的动作。
与STM32F7系列相比,STM32H7系列中,DMA部分有较大的改动,之前在F7上的DMA访问机制,在H7上无法复用。最大的来处不同为:
* ① 使用DMA MUX来为各个DMA通道选择外设,可由用户自由定义,不再严格受限于硬件指定
* ② DMA可访问地址受到限制。由于H7内核可运行在480MHz,内部的SRAM并没有全部运行在内核频率下,只有部分地址区间被设计为全速480MHz运行,其他地址空间运行在200MHz的低速。因此DMA的可访问地址,与能够与外设互通的内存,被限制在低速内存地址空间,而不是任意地址范围。这个变化是最坑的地方,如果按照之前ST的MCU使用经验,直接将变量地址设置为DMA访问内存地址,如果地址空间速度不匹配,将会出现不报任何错误的情况下,DMA收不到数据,调死也出不来!!!
未完待续… …
(4) 采样触发接口
因为是定时触发采样,所以设计一个采样触发接口,启动ADC采样转换。该接口可在定时任务中,每隔100毫秒调用一次。每次转换完成后,ADC自动停止采样。
void TriggerChipADCConversion()
{
if ((LL_ADC_IsEnabled(TEMP_ADC) == 1) &&
(LL_ADC_IsDisableOngoing(TEMP_ADC) == 0) &&
(LL_ADC_REG_IsConversionOngoing(TEMP_ADC) == 0) )
{
LL_DMA_ClearFlag_TC7(TEMP_ADC_DMA_LL);
LL_DMA_ClearFlag_TE7(TEMP_ADC_DMA_LL);
LL_DMA_SetDataLength(TEMP_ADC_DMA_LL, TEMP_ADC_DMA_STREAM_LL, TEMP_ADC_CHANNELS);
LL_DMA_EnableStream(TEMP_ADC_DMA_LL, TEMP_ADC_DMA_STREAM_LL);
LL_ADC_REG_StartConversion(TEMP_ADC);
}
}
一轮转换完成后,会触发DMA传输中断,在中断处理中,做采样完成的通知操作:
void TEMP_ADC_DMA_IRQHandler()
{
if(LL_DMA_IsActiveFlag_TC7(TEMP_ADC_DMA_LL))
{
LL_DMA_DisableStream(TEMP_ADC_DMA_LL, TEMP_ADC_DMA_STREAM_LL);
TriggerTempCtrlEvent(0x01);
LL_DMA_ClearFlag_TC7(TEMP_ADC_DMA_LL);
}
if(LL_DMA_IsActiveFlag_FE7(TEMP_ADC_DMA_LL))
{
LL_DMA_ClearFlag_FE7(TEMP_ADC_DMA_LL);
}
if(LL_DMA_IsActiveFlag_TE7(TEMP_ADC_DMA_LL))
{
LL_DMA_ClearFlag_TE7(TEMP_ADC_DMA_LL);
}
}
未完待续… …
2. MCU串口传输
未完待续… …