本帖最后由 穿西装的强子 于 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算法
|
打赏榜单
ArteryMCU 打赏了 30.00 元 2025-07-04 理由:[M412开发板评测活动]内容优质
|