@@ -14,7 +14,6 @@
# include "systick.h"
# include <cstdint>
# include <cstring>
# include <gd32c10x_dma.h>
// ADC 换算常量均为编译期常量,非可变全局状态。
// 用于 ADC 原始值到电压、温度、电流的固定比例换算。
@@ -31,9 +30,15 @@ extern CAN_transmiter CAN0_transmiter;
extern FOC_Controller FOC ;
// 全局采样器单例:系统只有一组 ADC 采样缓存,由 hardware_init() 调用 sampler.init() 初始化。
// regular DMA 写 adc_value[]; injected ADC 中断写 adc_current_value[] 并置位 adc_current_frame_ready。
// ADC0 只跑 injected 组(PWM 同步):中断写 adc_current_value[]( U/V/W)与
// adc_mcu_temp_raw(温度,挂注入序列尾部同步转换)并置位 adc_current_frame_ready。
// VBUS / DRV温度 由 ADC1 在慢速任务中软件触发轮询,与电流采样彻底隔离。
// 背景:此前温度/VBUS/DRV 放在 ADC0 regular 组连续扫描,与 injected 抢占同一 ADC。
// 温度通道提到 239.5 周期后,稳态下注入触发几乎必然打断温度采样中段:
// regular 被打断后从 rank0 重启而 DMA 计数不复位 → adc_value[] 数据错位;
// 采样电容长期挂在高内阻温度源电压上,干扰注入组首通道 → 相电流检测故障。
sampler sampler ; // 采样器初始化
u16 adc_value [ 3 ] = { 0 } ; // regular DMA: MCU温度 / DRV温度 / VBUS
volatile u16 adc_mcu_temp_raw = 0 ; // ADC0 injected rank3: MCU温度原始值
static volatile u16 adc_current_value [ 3 ] = { 0 } ; // injected 同步采样: U / V / W
static volatile u8 adc_current_frame_ready = 0 ;
@@ -47,83 +52,94 @@ static inline float current_adc_to_ma(u16 raw_adc) {
}
/*!
\brief configure the DMA peripheral
\brief configure the ADC1 peripheral( VBUS / DRV温度 专用)
\param[in] none
\param[out] none
\retval none
\note ADC1 独占慢速量:单通道单次软件触发,无 DMA、无中断、无连续模式,
软件按需轮询读取,与 ADC0 的 PWM 同步电流采样互不干扰。
*/
void dma_config ( void ) {
/* enable DMA0 clock */
rcu_periph_clock_enable ( RCU_DMA0 ) ;
/* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter ;
void adc1_config ( void ) {
/* enable ADC1 clock(ADC 时钟分频与 ADC0 共用 RCU 配置) */
rcu_periph_clock_enable ( RCU_ADC1 ) ;
/* ADC DMA_channel configuration */
dma_deinit ( DMA0 , DMA_CH0 ) ;
adc_deinit ( ADC1 ) ;
adc_mode_config ( ADC_MODE_FREE ) ;
adc_data_alignment_config ( ADC1 , ADC_DATAALIGN_RIGHT ) ;
/* initialize DMA single data mode */
dma_data_parameter . periph_addr = ( uint32_t ) ( & ADC_RDATA ( ADC0 ) ) ;
dma_data_parameter . periph_inc = DMA_PERIPH_INCREASE_DISABLE ;
dma_data_parameter . memory_addr = ( uint32_t ) ( adc_value ) ;
dma_data_parameter . memory_inc = DMA_MEMORY_INCREASE_ENABLE ;
dma_data_parameter . periph_width = DMA_PERIPHERAL_WIDTH_16BIT ;
dma_data_parameter . memory_width = DMA_MEMORY_WIDTH_16BIT ;
dma_data_parameter . direction = DMA_PERIPHERAL_TO_MEMORY ;
dma_data_parameter . number = 3 ;
dma_data_parameter . priority = DMA_PRIORITY_HIGH ;
dma_init ( DMA0 , DMA_CH0 , & dma_data_parameter ) ;
/* 单通道单次转换:调用方每次配置 rank0 通道后软件触发 */
adc_channel_length_config ( ADC1 , ADC_REGULAR_CHANNEL , 1 ) ;
adc_external_trigger_source_config ( ADC1 , ADC_REGULAR_CHANNEL ,
ADC0_1_EXTTRIG_REGULAR_NONE ) ;
adc_external_trigger_config ( ADC1 , ADC_REGULAR_CHANNEL , ENABLE ) ;
dma_circulation_enable ( DMA0 , DMA_CH0 ) ;
adc_enable ( ADC1 ) ;
/* ADC校准 */
adc_calibration_enable ( ADC1 ) ;
}
/* enable DMA channel */
dma_channel_enable ( DMA0 , DMA_CH0 ) ;
/**
* @brief ADC1 单次软件触发转换并等待完成
* @param[in] channel ADC 通道号(如 ADC_CHANNEL_6 / ADC_CHANNEL_9)
* @return u16 转换原始值(超时返回上次 RDATA 残留值)
* @note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发)。
* 单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。
*/
u16 adc1_read_raw ( u8 channel ) {
u16 timeout = 0 ;
adc_flag_clear ( ADC1 , ADC_FLAG_EOC ) ;
adc_regular_channel_config ( ADC1 , 0 , channel , ADC_SAMPLETIME_28POINT5 ) ;
adc_software_trigger_enable ( ADC1 , ADC_REGULAR_CHANNEL ) ;
while ( adc_flag_get ( ADC1 , ADC_FLAG_EOC ) = = RESET ) {
if ( + + timeout > 2000U ) { // 硬件异常保护,避免死等
break ;
}
}
adc_flag_clear ( ADC1 , ADC_FLAG_EOC ) ;
return ( u16 ) adc_regular_data_read ( ADC1 ) ;
}
/*!
\brief configure the ADC peripheral
\brief configure the ADC0 peripheral( PWM 同步电流采样专用)
\param[in] none
\param[out] none
\retval none
\note ADC0 只保留 injected 组,regular 组整体移除:
此前 regular 连续扫描与 injected 触发抢占同一 ADC,注入打断后
regular 从头重启而 DMA 不复位导致数据错位,且温度长采样被打断后
采样电容残余污染注入组首通道。移除 regular 后注入组再无打断源。
*/
void adc_config ( void ) {
/* enable ADC clock */
rcu_periph_clock_enable ( RCU_ADC0 ) ;
/* config ADC clock */
/* config ADC clock( ADC0/ADC1 共用分频:120MHz/4 = 30MHz) */
rcu_adc_clock_config ( RCU_CKADC_CKAPB2_DIV4 ) ;
/* 复位ADC */
adc_deinit ( ADC0 ) ;
/* ADC模式配置 */
adc_mode_config ( ADC_MODE_FREE ) ;
/* regular 组保留连续扫描,用于温度/VBUS 后台采样 */
adc_special_function_config ( ADC0 , ADC_CONTINUOUS_MODE , ENABLE ) ;
/* 扫描模式:注入序列内 4 通道触发后自动连续转换 */
adc_special_function_config ( ADC0 , ADC_SCAN_MODE , ENABLE ) ;
/* injected 组用于 PWM 同步电流采样,关闭自动串联,独立由 TIMER1 触发 */
adc_special_function_config ( ADC0 , ADC_INSERTED_CHANNEL_AUTO , DISABLE ) ;
/* ADC数据右对齐配置 */
adc_data_alignment_config ( ADC0 , ADC_DATAALIGN_RIGHT ) ;
/* 使能ADC温度传感器和 内部参考电压 */
/* 使能ADC温度传感器( 内部通道仅连接 ADC0 的 ch16/ch17) */
adc_tempsensor_vrefint_enable ( ) ;
/* 配置ADC通道长度 */
adc_channel_length_config ( ADC0 , ADC_REGULAR_CHANNEL , 3 ) ;
/* 配置ADC规则通道 */
adc_regular_channel_config ( ADC0 , 0 , ADC_CHANNEL_16 ,
ADC_SAMPLETIME_41POINT5 ) ;
adc_regular_channel_config ( ADC0 , 1 , ADC_CHANNEL_9 , ADC_SAMPLETIME_28POINT5 ) ;
adc_regular_channel_config ( ADC0 , 2 , ADC_CHANNEL_6 , ADC_SAMPLETIME_28POINT5 ) ;
/* regular 组继续软件触发连续转换:本组只服务低速量 */
adc_external_trigger_source_config ( ADC0 , ADC_REGULAR_CHANNEL ,
ADC0_1_EXTTRIG_REGULAR_NONE ) ;
adc_external_trigger_config ( ADC0 , ADC_REGULAR_CHANNEL , ENABLE ) ;
/* injected 组改为 TIMER1 CH3 导出的 TRGO 触发,实现三相同一采样时刻锁存 */
/* injected 组:rank0=U, rank1=V, rank2=W, rank3=MCU温度,TIMER1 TRGO 触发。
* 温度传感器(ch16)为高内阻源,数据手册要求采样时间≥17.1μs,保持最大档
* 239.5 周期,且挂在序列尾部:整段转换不会被打断,每 PWM 周期同步采一次,
* 40kHz 过采样 + 中值滤波后温度比旧架构更稳。
* 原先的 rank0 dummy 已删除:dummy 用于吸收"注入打断 regular"产生的采样
* 电容残余电荷,现 regular 已移除,序列空闲期电容挂在温度通道(~1.3V),
* U 相(ch3 运放低阻输出) 的 55.5 周期采样足以完成重建。
* 序列总时长 68× 3+252 = 456 周期 = 15.2μs < 25μs 触发周期。 */
adc_channel_length_config ( ADC0 , ADC_INSERTED_CHANNEL , 4 ) ;
adc_inserted_channel_config ( ADC0 , 0 , ADC_CHANNEL_3 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 1 , ADC_CHANNEL_3 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 2 , ADC_CHANNEL_4 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 3 , ADC_CHANNEL_5 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 1 , ADC_CHANNEL_4 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 2 , ADC_CHANNEL_5 , ADC_SAMPLETIME_55POINT5 ) ;
adc_inserted_channel_config ( ADC0 , 3 , ADC_CHANNEL_16 ,
ADC_SAMPLETIME_239POINT5 ) ;
adc_external_trigger_source_config ( ADC0 , ADC_INSERTED_CHANNEL ,
ADC0_1_EXTTRIG_INSERTED_T1_TRGO ) ;
adc_external_trigger_config ( ADC0 , ADC_INSERTED_CHANNEL , ENABLE ) ;
@@ -132,19 +148,15 @@ void adc_config(void) {
adc_enable ( ADC0 ) ;
/* ADC校准 */
adc_calibration_enable ( ADC0 ) ;
/* 使能ADC DMA功能 */
adc_dma_mode_enable ( ADC0 ) ;
/* injected 完成中断负责搬运完整三相样本 */
/* injected 完成中断负责搬运完整三相样本与温度原始值 */
adc_interrupt_enable ( ADC0 , ADC_INT_EOIC ) ;
/* 先启动 regular 组连续后台采样 */
adc_software_trigger_enable ( ADC0 , ADC_REGULAR_CHANNEL ) ;
}
/**
* @brief 采样类初始化
*/
u8 sampler : : init ( ) {
dma_config ( ) ; // DMA采样ADC初始化
adc_config ( ) ; // ADC初始化
adc_config ( ) ; // ADC0: PWM 同步注入组(U/V/W + MCU温度)
adc1_config ( ) ; // ADC1: VBUS / DRV温度 慢速轮询
current_sample_head = 0 ;
adc_current_frame_ready = 0 ;
delay_1ms ( 1 ) ;
@@ -154,15 +166,16 @@ u8 sampler::init() {
// 初始采样
for ( int i = 0 ; i < ADC_FILTER_SIZE ; i + + ) {
mcu_temp_samples [ i ] =
( 1.43f - adc_value [ 0 ] * ADC_SCALE ) * TEMP_FACTOR + 35.0f ;
( 1.43f - adc_mcu_temp_raw * ADC_SCALE ) * TEMP_FACTOR + 35.0f ;
currU_samples [ i ] = current_init ;
currV_samples [ i ] = current_init ;
currW_samples [ i ] = current_init ;
vbus_samples [ i ] = adc_value [ 2 ] * ADC_SCALE * VCC_FACTOR ;
vbus_samples [ i ] = adc1_read_raw ( ADC_CHANNEL_6 ) * ADC_SCALE * VCC_FACTOR ;
float R_now = ( float ) adc_value [ 1 ] / ( 4096 - ( float ) adc_value [ 1 ] ) * 4700 ;
const float drv_raw = ( float ) adc1_read_raw ( ADC_CHANNEL_9 ) ;
float R_now = drv_raw / ( 4096 - drv_raw ) * 4700 ;
drv_temp_samples [ i ] =
1.0f / ( 0.00335f + 0.00025f * log ( R_now / 10000.0f ) ) - 273.15f ;
delay_1ms ( 1 ) ;
@@ -243,7 +256,7 @@ float sampler::get_vbus_voltage() {
void sampler : : update_mcu_temp ( ) {
// 环形缓冲区写入,替代 memmove
mcu_temp_samples [ mcu_temp_head ] =
( 1.43f - adc_value [ 0 ] * ADC_SCALE ) * TEMP_FACTOR + 35.0f ;
( 1.43f - adc_mcu_temp_raw * ADC_SCALE ) * TEMP_FACTOR + 35.0f ;
mcu_temp_head + + ;
if ( mcu_temp_head > = ADC_FILTER_SIZE ) mcu_temp_head = 0 ;
@@ -260,8 +273,10 @@ void sampler::update_mcu_temp() {
* @brief 更新MCU温度
*/
void sampler : : update_drv_temp ( ) {
// ADC1 单次轮询转换 ch9(NTC 分压,低阻源)
const float drv_raw = ( float ) adc1_read_raw ( ADC_CHANNEL_9 ) ;
// 环形缓冲区写入,替代 memmove
float R_now = ( float ) adc_value [ 1 ] / ( 4096 - ( float ) adc_value [ 1 ] ) * 4700 ;
float R_now = drv_raw / ( 4096 - drv_raw ) * 4700 ;
drv_temp_samples [ drv_temp_head ] =
1.0f / ( 0.00335f + 0.00025f * log ( R_now / 10000.0f ) ) - 273.15f ;
drv_temp_head + + ;
@@ -300,8 +315,10 @@ void sampler::update_current() {
* @brief 更新供电电压
*/
void sampler : : update_vbus_voltage ( ) {
// ADC1 单次轮询转换 ch6(电阻分压,低阻源)
// 环形缓冲区写入,替代 memmove
vbus_samples [ temp_vbus_head ] = adc_value [ 2 ] * ADC_SCALE * VCC_FACTOR ;
vbus_samples [ temp_vbus_head ] =
adc1_read_raw ( ADC_CHANNEL_6 ) * ADC_SCALE * VCC_FACTOR ;
temp_vbus_head + + ;
if ( temp_vbus_head > = ADC_FILTER_SIZE ) temp_vbus_head = 0 ;
@@ -321,11 +338,11 @@ void sampler::update_vbus_voltage() {
*/
extern " C " void ADC0_1_IRQHandler ( void ) {
if ( adc_interrupt_flag_get ( ADC0 , ADC_INT_FLAG_EOIC ) ! = RESET ) {
/* 丢弃 rank0 dummy,真实样本使用 rank1/2/3,对应 U/V/W 同一 PWM 时刻 */
( void ) adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_0 ) ;
adc_current_value [ 0 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_1 ) ;
adc_current_value [ 1 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_2 ) ;
adc_current_value [ 2 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_3 ) ;
/* rank0/1/2 = U/V/W 同一 PWM 时刻样本,rank3 = MCU温度(序列尾部) */
adc_current_value [ 0 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_0 ) ;
adc_current_value [ 1 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_1 ) ;
adc_current_value [ 2 ] = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_2 ) ;
adc_mcu_temp_raw = adc_inserted_data_read ( ADC0 , ADC_INSERTED_CHANNEL_3 ) ;
adc_current_frame_ready = 1U ;
adc_interrupt_flag_clear ( ADC0 , ADC_INT_FLAG_EOIC ) ;