本帖最后由 穿西装的强子 于 2025-6-7 20:47 编辑
#申请原创# @21小跑堂
上一篇已经准备好了foc的代码,现在准备移植。
第一步:
PWM移植
根据上一篇的配置要求,使用中央对齐模式生成PWM,最好使用互补PWM,带死区高级定时器,死区时间一般是>50ns,根据自己的定时间配置即可。
因为我使用的simplefoc驱动板不需要互补PWM,使用IR2104作为驱动芯片,只需要单向PWM即可同时驱动上下桥mos。
原理图如下图,通过IN1,IN2,IN3输入PWM,SD#上拉使能即可驱动上下桥mos。
PWM设置中央对齐模式,PWM配置为10KHZ,通道1~通道3作为IN1,IN2,IN3的输入,通道4作为ADC采样触发源
WorkBench配置如下图
PWM配置代码如下:
使用TIMER4作为驱动定时器,TIMER4和TIMER1同样是16位定时器,除了互补PWM功能,其它基本一致。
使用TIMER4还有个原因是TIMER1要占用串口PA9和PA10,这个我用了串口,而且我也不需要互补PWM,所以就用了TIMER4。
gpio_init_type gpio_init_struct;
tmr_output_config_type tmr_output_struct;
gpio_default_para_init(&gpio_init_struct);
/* add user code begin tmr4_init 1 */
crm_periph_clock_enable(CRM_TMR4_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
/* add user code end tmr4_init 1 */
/* configure the tmr4 CH1 pin */
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE6, GPIO_MUX_2);
gpio_init_struct.gpio_pins = GPIO_PINS_6;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
gpio_init(GPIOB, &gpio_init_struct);
/* configure the tmr4 CH2 pin */
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE7, GPIO_MUX_2);
gpio_init_struct.gpio_pins = GPIO_PINS_7;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
gpio_init(GPIOB, &gpio_init_struct);
/* configure the tmr4 CH3 pin */
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE8, GPIO_MUX_2);
gpio_init_struct.gpio_pins = GPIO_PINS_8;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
gpio_init(GPIOB, &gpio_init_struct);
/* configure counter settings */
tmr_cnt_dir_set(TMR4, TMR_COUNT_TWO_WAY_1);
tmr_clock_source_div_set(TMR4, TMR_CLOCK_DIV1);
tmr_period_buffer_enable(TMR4, FALSE);
tmr_base_init(TMR4, 999, 8);
/* configure primary mode settings */
tmr_sub_sync_mode_set(TMR4, FALSE);
tmr_primary_mode_select(TMR4, TMR_PRIMARY_SEL_RESET);
/* configure channel 1 output settings */
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_output_struct.oc_output_state = TRUE;
tmr_output_struct.occ_output_state = FALSE;
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.occ_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.oc_idle_state = FALSE;
tmr_output_struct.occ_idle_state = FALSE;
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_1, &tmr_output_struct);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_1, 700);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_1, FALSE);
/* configure channel 2 output settings */
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_output_struct.oc_output_state = TRUE;
tmr_output_struct.occ_output_state = FALSE;
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.occ_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.oc_idle_state = FALSE;
tmr_output_struct.occ_idle_state = FALSE;
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_2, &tmr_output_struct);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_2, 300);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_2, FALSE);
/* configure channel 3 output settings */
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_output_struct.oc_output_state = TRUE;
tmr_output_struct.occ_output_state = FALSE;
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.occ_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.oc_idle_state = FALSE;
tmr_output_struct.occ_idle_state = FALSE;
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_3, &tmr_output_struct);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_3, 100);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_3, FALSE);
/* configure channel 4 output settings */
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_B;
tmr_output_struct.occ_output_state = FALSE;
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.occ_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_output_struct.oc_idle_state = FALSE;
tmr_output_struct.occ_idle_state = FALSE;
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_4, &tmr_output_struct);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_4, 1);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_4, FALSE);
tmr_output_enable(TMR4, TRUE);
tmr_counter_enable(TMR4, TRUE);
PWM波形图如下所示绿色为通道1,黄色为通道2
ADC配置
我找了AT32M412的资料,没有发现注入模式,只有触发模式,因此使用TMR4_CH4进行触发ADC中断,进行ADC采集。
根据原理图配置了2个通道,驱动板只做了双电阻采样,没有做三电阻采样,所以使用2路ADC即可;
使用了INA240A2作为电流采样芯片。
原理图如下
使用TIMER4_CH4作为触发源,触发模式为下降沿触发
代码如下
void wk_adc1_init(void)
{
/* add user code begin adc1_init 0 */
/* add user code end adc1_init 0 */
gpio_init_type gpio_init_struct;
adc_base_config_type adc_base_struct;
adc_common_config_type adc_common_struct;
gpio_default_para_init(&gpio_init_struct);
/* add user code begin adc1_init 1 */
/* add user code end adc1_init 1 */
/*gpio--------------------------------------------------------------------*/
/* configure the IN0 pin */
gpio_init_struct.gpio_mode = GPIO_MODE_ANALOG;
gpio_init_struct.gpio_pins = GPIO_PINS_0;
gpio_init(GPIOA, &gpio_init_struct);
/* configure the IN4 pin */
gpio_init_struct.gpio_mode = GPIO_MODE_ANALOG;
gpio_init_struct.gpio_pins = GPIO_PINS_4;
gpio_init(GPIOA, &gpio_init_struct);
crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
nvic_irq_enable(ADC1_2_IRQn, 0, 0);
adc_common_default_para_init(&adc_common_struct);
/* config combine mode */
adc_common_struct.combine_mode = ADC_INDEPENDENT_MODE;
/* config division,adcclk is division by hclk */
adc_common_struct.div = ADC_HCLK_DIV_6;
/* config common dma mode,it's not useful in independent mode */
adc_common_struct.common_dma_mode = ADC_COMMON_DMAMODE_DISABLE;
/* config common dma request repeat */
adc_common_struct.common_dma_request_repeat_state = FALSE;
/* config adjacent adc sampling interval,it's useful for ordinary shifting mode */
adc_common_struct.sampling_interval = ADC_SAMPLING_INTERVAL_4CYCLES;
/* config inner temperature sensor and vintrv */
adc_common_struct.tempervintrv_state = FALSE;
adc_common_config(&adc_common_struct);
adc_base_default_para_init(&adc_base_struct);
adc_base_struct.sequence_mode = TRUE;
adc_base_struct.repeat_mode = FALSE;
adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;
adc_base_struct.ordinary_channel_length = 2;
adc_base_config(ADC1, &adc_base_struct);
adc_resolution_set(ADC1, ADC_RESOLUTION_12B);
/* config ordinary channel */
adc_ordinary_channel_set(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_1_5);
adc_ordinary_channel_set(ADC1, ADC_CHANNEL_4, 2, ADC_SAMPLETIME_1_5);
/* config ordinary trigger source and trigger edge */
adc_ordinary_conversion_trigger_set(ADC1, ADC_ORDINARY_TRIG_TMR4CH4, ADC_ORDINARY_TRIG_EDGE_FALLING);
/* config dma mode,it's not useful when common dma mode is use */
adc_dma_mode_enable(ADC1, FALSE);
/* config dma request repeat,it's not useful when common dma mode is use */
adc_dma_request_repeat_enable(ADC1, FALSE);
/* each ordinary channel conversion set occe flag */
adc_occe_each_conversion_enable(ADC1, FALSE);
/* enable adc ordinary channels conversion end interrupt */
adc_interrupt_enable(ADC1, ADC_OCCE_INT, TRUE);
/* enable adc overflow interrupt */
adc_interrupt_enable(ADC1, ADC_OCCO_INT, TRUE);
/* enable adc trigger convert fail interrupt */
adc_interrupt_enable(ADC1, ADC_TCF_INT, TRUE);
/* enable adc trigger conversion fail auto conversion abort */
adc_convert_fail_auto_abort_enable(ADC1, TRUE);
/* adc enable */
adc_enable(ADC1, TRUE);
while(adc_flag_get(ADC1, ADC_RDY_FLAG) == RESET);
/* adc calibration */
adc_calibration_init(ADC1);
while(adc_calibration_init_status_get(ADC1));
adc_calibration_start(ADC1);
while(adc_calibration_status_get(ADC1));
adc_ordinary_software_trigger_enable(ADC1, TRUE);
}
/* add user code end 1 */
void ADC1_2_IRQHandler(void)
{
if(adc_interrupt_flag_get(ADC1, ADC_TCF_FLAG) != RESET)
{
adc_flag_clear(ADC1, ADC_TCF_FLAG);
adc1_conversion_fail_flag++;
}
if(adc_interrupt_flag_get(ADC1, ADC_OCCO_FLAG) != RESET)
{
adc_flag_clear(ADC1, ADC_OCCO_FLAG);
adc1_overflow_flag++;
}
if(adc_interrupt_flag_get(ADC1, ADC_OCCE_FLAG) != RESET)
{
adc_flag_clear(ADC1, ADC_OCCE_FLAG);
adc1_ordinary_conversion_flag++;
adc1_ordinary_valuetab[0] = adc_common_ordinary_data_get(ADC1, 1);
adc1_ordinary_valuetab[1] = adc_common_ordinary_data_get(ADC1, 2);
gpio_bits_toggle(GPIOA,GPIO_PINS_8);
}
}
这个地方调试了一会,ADC触发始终没在PWM的中间,主要是使用定时器触发的时候,TIMER4_ch4的PWM模式需要和TIMER4_CH1/2/3模式不一样,CH1、2、3使用PWMA模式,CH4使用PWMB模式,反极性的配置,CH4配置无引脚输出,占空比为1,因为是中央对齐模式,这样CH4的PWM信号就会在CH1/2/3的中间,ADC触发采样的时间就会在CH1/2/3的PWM中间,也就是在上桥MOS打开的时间段最中间进行采集,这样的话采集的电流是最精准的。
下图是PWM和ADC触发的波形图,黄色为PWM。绿色为ADC触发的信号(触发时翻转GOIO)。
最后一张是最终正常的触发方式。
ADC采集值
foc的算**在adc中断内进行运行,因此adc的中断优先级设置为最高。
下一篇开始移植foc算法
|
|