打印
[活动专区]

【AT-START-M412测评】+ 无刷电机控制3-svpwm和ADC采样

[复制链接]
69|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 穿西装的强子 于 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算法



使用特权

评论回复
沙发
yangxiaor520| | 2025-6-7 22:14 | 只看该作者
沙发,坐等后续更新。

使用特权

评论回复
板凳
穿西装的强子|  楼主 | 2025-6-7 22:30 | 只看该作者
yangxiaor520 发表于 2025-6-7 22:14
沙发,坐等后续更新。

感谢关注

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

45

主题

193

帖子

3

粉丝