这篇博客只要讲怎么用stm32 LL库 dma多通道ADC采样,实现两路电流,一路NTC温感(用的SDNT1608X103F3380FTF),一路电源电压采样。
一、stm32cudeMX的一些配置
1、配置程序下载方式
这里选择sw,手头上只有jliink-ob,将就的用着先。
2、配置adc
Continuous Conversion Mode这个选项有点看不懂,我试过在dma为Normal模式下关掉,发现数据就收不到了。按字面意思是连续转换模式,我关掉,触发完一次,我在中断里再开就能一直触发才对的啊,实际好像不是这样,暂时不管这个问题(mark一下),哪天想明白回来说说。 adc我开了4个通道采集,其中两路用来采集电流,一路采集温度,最后一路采集电源电压。
这个是配置采集的通道的顺序,方便后面计算。
GPIO的配置
DMA的模式我选的是Normal这个,另外还有一个Circular。如果选Circular的话,Continuous Conversion Mode这个选项一定是要开的!不过我用Normal也有我的道理,我可以在中断中把转换关了,等处理完数据再打开,确保数据的实时性。如果用Circular,我还得开个定时器,定时去读,总感觉实时性没另外这个好。
3、配置时钟源
这里啥都没选,我画的那个板子没有外接晶振,全靠芯片内部晶振为爱工作了。
4、配置时钟树
这里我配置的时钟频率为64M,有见过超频到100M的,貌似也能用,不过功耗和寿命可能就短一点。
5、其他配置
这里我一般都这么选,根据自己需要可以改
这里由于我最近在学LL库,所以全选LL慢慢从坑里爬出来
我用的库是这个版本的,其他版本可能不一样
二、代码实现
1、ADC和DMA的初始化
void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
LL_ADC_InitTypeDef ADC_InitStruct = {
0};
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {
0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {
0};
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_SYSCLK);
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
/**ADC1 GPIO Configuration
PA0 ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
*/
GPIO_InitStruct.Pin = ISENSC_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(ISENSC_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = ISENSB_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(ISENSB_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = TEMP_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(TEMP_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = VBUS_S_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(VBUS_S_GPIO_Port, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
/* USER CODE BEGIN ADC1_Init 1 */
//这里配置需要接收多少数据
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_LEN);
//告诉DMA要把数据搬到哪里,这里是adcData
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)adcData);
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA));
//开启接收完成中断
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
#define ADC_CHANNEL_CONF_RDY_TIMEOUT_MS ( 1U)
#if (USE_TIMEOUT == 1)
uint32_t Timeout ; /* Variable used for Timeout management */
#endif /* USE_TIMEOUT */
ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
LL_ADC_Init(ADC1, &ADC_InitStruct);
LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);
/* Poll for ADC channel configuration ready */