Loading... ## STM32 cubemx配置 ### STM32 ADC配置 1. 配置ADC channel, IN0 + IN5 2. Parameter Settings ``` Resolution: 12bit -> 4096 max Data Aliginment: Right alignment Scan Conversion Mode: Enable Continuous Conversion Mode: Disable Discontinuous Conversion Mode: Disable DMA Continuous Mode: Enable End Of Conversion Selection: EOC flag at the end of all conversions Number Of Conversion: 2 External Trigger Conversion Source: Timer 2 Trigger Out event External Trigger Conversion Edge: Trigger detection on the rising edge Rank 1 Channel: Channel 0 Sampling Time: 15 Cycles Rank 2 Chennel: Channel 5 Sampling Time: 15 Cycles ``` 3. DMA Settings ``` DMA Request: ADC1 Stream: DMA2 Stream 0 Direction: Peripheral To Memory Priority: Low ``` ### STM32 TIM2配置 1. Parameter Settings ``` Clocl Souce: Internal Clock Trigger Event Selection: Update Event ``` ## 初始化代码 ### TIM2初始化函数 ```c void MX_TIM2_Init(void) { /* 初始化TIM2配置,预分频系数为41,向上计数模式,自动重载值为49, 同步时钟不分频,并启用自动重载预装载功能 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 41; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 49; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } } ``` ### ADC1初始化函数 ```c void MX_ADC1_Init(void) { ADC_AnalogWDGConfTypeDef AnalogWDGConfig = {0}; ADC_ChannelConfTypeDef sConfig = {0}; // 配置ADC1全局属性,包括时钟预分频、分辨率、扫描模式、触发源、数据对齐方式等 hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = ENABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 2; hadc1.Init.DMAContinuousRequests = ENABLE; hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } // 配置模拟看门狗,单通道监控,阈值设定,以及中断模式启用 AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG; AnalogWDGConfig.HighThreshold = 3723; AnalogWDGConfig.LowThreshold = 0; AnalogWDGConfig.Channel = ADC_CHANNEL_0; AnalogWDGConfig.ITMode = ENABLE; if (HAL_ADC_AnalogWDGConfig(&hadc1, &AnalogWDGConfig) != HAL_OK) { Error_Handler(); } // 分别配置通道0和通道5的具体采样时间和在序列中的位置 sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_5; sConfig.Rank = 2; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } } ``` ## 用户自定义代码 ### 创建数据缓冲区并启动ADC ```c // 创建一个大小为256的原始ADC数据缓冲区 uint16_t adc_raw[256]; // 启动ADC DMA模式采集200个样本,并启动TIM2 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw, 200); HAL_TIM_Base_Start(&htim2); ``` ### ADC转换完成回调函数 ```c // 定义存储电流和电压值的变量 uint32_t Channel_i_value, Channel_v_value; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { /* 200个数据,两通道各100次采样,取64次(前后各丢18次)*/ int drop_count = 18; int total_count = 100; int shift = 6; // 64 = 2^6 uint16_t max_i = 0; uint16_t min_i = 0xFFFF; uint16_t max_v = 0; uint16_t min_v = 0xFFFF; int max_i_index, min_i_index, max_v_index, min_v_index; uint32_t sum_raw; /* 停止TIM避免多通道数据错位的问题 */ HAL_ADC_Stop_DMA(&hadc1); HAL_TIM_Base_Stop(&htim2); #define i_raw(index) (raw[2 * (index) + 0]) #define v_raw(index) (raw[2 * (index) + 1]) // 找出电流和电压的最大值和最小值及其索引 for (int i = 0; i < drop_count; i++) { for (int j = i; j < (total_count - i); j++) { if (max_i < i_raw(j)) { max_i = i_raw(j); max_i_index = j; } if (min_i > i_raw(j)) { min_i = i_raw(j); min_i_index = j; } if (max_v < v_raw(j)) { max_v = v_raw(j); max_v_index = j; } if (min_v > v_raw(j)) { min_v = v_raw(j); min_v_index = j; } } max_i = i_raw(max_i_index); i_raw(max_i_index) = i_raw(i); i_raw(i) = max_i; min_i = i_raw(min_i_index); i_raw(min_i_index) = i_raw(total_count - i); i_raw(total_count - i) = min_i; max_v = v_raw(max_v_index); v_raw(max_v_index) = v_raw(i); v_raw(i) = max_v; min_v = v_raw(min_v_index); v_raw(min_v_index) = v_raw(total_count - i); v_raw(total_count - i) = min_v; } // 计算电流和电压的中位平均值 sum_raw = 0; for (int i = drop_count; i < (total_count - drop_count); i++) sum_raw += i_raw(i); // Channel 0 value for current Channel_i_value = sum_raw >> shift; sum_raw = 0; for (int i = drop_count; i < (total_count - drop_count); i++) sum_raw += v_raw(i); // Channel 5 value for voltage Channel_v_value = sum_raw >> shift; #undef i_raw #undef v_raw } // 获取ADC采样结果后,重新启动ADC和TIM2 void start_adc(void) { HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw, 200); HAL_TIM_Base_Start(&htim2); } ``` ### 总结 本文详细介绍了如何通过STM32CubeMX配置STM32的ADC以配合DMA实现周期性采样,并通过用户自定义代码实现了数据滤波。具体而言,配置了ADC1进行IN0和IN5两个通道的采样,并借助TIM2作为触发源,在每次触发事件发生时执行两次ADC采样。利用DMA将采集到的数据连续传送到内存缓冲区,同时在ADC转换完成后调用回调函数,该函数会对采集的数据进行初步处理,包括数据筛选和中位平均计算,从而实现对电流和电压的准确测量。最后,通过start_adc()函数循环启动ADC和TIM2以持续进行周期采样。 最后修改:2024 年 03 月 09 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏