美文网首页
如何构建一块电路板的护城河1_ADC的DMA读取和处理

如何构建一块电路板的护城河1_ADC的DMA读取和处理

作者: 吴松乾 | 来源:发表于2019-01-27 00:38 被阅读0次

    如何构建一块电路板的护城河1_ADC的DMA读取和处理

    STM32 编程

    通过MXCUBE可以设置的参数如下:


    MXCUBE生成界面.png

    初始化函数如下:

     hadc1.Instance = ADC1;
      hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;     //ADC的频率不建议太高
      hadc1.Init.Resolution = ADC_RESOLUTION_12B; //精度
      hadc1.Init.ScanConvMode = ENABLE; //使能,则是顺序转换模式,不再有插队情况
      hadc1.Init.ContinuousConvMode = ENABLE; //连续转换,否则只转换一次
      hadc1.Init.DiscontinuousConvMode = DISABLE; //连续转换,当然就不间断了
      hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;//外部触发的边沿,这里NONE
      hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; //不需要外部触发
      hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;  //低位对齐
      hadc1.Init.NbrOfConversion = 8;  //8个通道
      hadc1.Init.DMAContinuousRequests = ENABLE; //连续不断写入DMA
      hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; 
    

    ADC进行初始化以后,在MSP中会对开辟的DMA进行初始化,如下:

     hdma_adc1.Instance = DMA2_Stream0;
        hdma_adc1.Init.Channel = DMA_CHANNEL_0;
        hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
        hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_adc1.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_adc1.Init.Mode = DMA_CIRCULAR;
        hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    

    在下面会有一条,将DMA和ADC连接起来。

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
    

    这样,将ADC1采样得到的数据,会自动的保存到dma的内存中。

    MX还有一个任务,就是中断设置,如下:

    {
      HAL_ADC_IRQHandler(&hadc1);
    }
        HAL_DMA_IRQHandler(&hdma_adc1);
    
    

    所以实际上,我们没有进行中断的处理,在初始化函数中,调用

    uint32_t ADC_ConvertedValue[ADC_NUMOFCHANNEL];
    ……
    HAL_ADC_Start_DMA(&hadc1,ADC_ConvertedValue,ADC_NUMOFCHANNEL);  
    

    这样,当ADC8个通道的值采集完成以后,自动会交给DMA,而DMA直接将数据保存到ADC_ConvertedValue中。其中ADC_ConvertedValue是DMA设置的数据宽度,一个8个通道的值。

    我们先定义一个数据结构体,用来保存从数组中读出来的中间值,并不是和DMA通道物理对应,如下:

    typedef struct
    {
      int32_t motor2_U_current;
      int32_t motor2_V_current;
      int32_t motor2_W_current;
      int32_t motor1_U_current;
      int32_t motor1_V_current;
      int32_t motor1_W_current;
      int32_t bus_volt;
      int32_t controller_temp;
    }DMA_AD_VALUE;
    

    并在头文件中申明一下结构体:并在C文件中进行定义

    extern DMA_AD_VALUE dma_ad;
    DMA_AD_VALUE dma_ad;
    

    同时我们设置一个结构体,用来保存一些中间变量,如下:

    typedef struct {
             int32_t   BUS_Curr ;     // DC Bus  Current
             int32_t   PhaseU_Curr;   // Phase A Current
             int32_t   PhaseV_Curr;   // Phase A Current
             int32_t   BUS_Voltage ;  //DC Bus  Voltage      
             int32_t   RP_speed_Voltage ;     //  RP1_Voltage
             int32_t   OffsetBUS_Curr ;     // DC Bus  Current
             int32_t   OffsetPhaseU_Curr;   // Phase A Current
             int32_t   OffsetPhaseV_Curr;   // Phase A Current
             int32_t   Coeff_filterK1;   // Phase A Current
               int32_t   Coeff_filterK2;   // Phase A Current 
           }ADCSamp;
    #define  ADCSamp_DEFAULTS  {0,0,0,0,0,0,0,0,268,756}
    

    有两个电机,所以我们申明和定义一个结构体变量如下:

    extern ADCSamp      ADCSampPareM1;  // M1 refer to tim8
    extern ADCSamp      ADCSampPareM2;  // M2 refer to tim1
    
    ADCSamp      ADCSampPareM1=ADCSamp_DEFAULTS;
    ADCSamp      ADCSampPareM2=ADCSamp_DEFAULTS;
    

    首先,我们要读取所有模拟值的初始化值,初始化我们通过连续读取32次求平均值来获得,如下:

    //校准作用,电流传感器的偏移值为1.65V
    void Offset_CurrentReading(void)
    {
        static uint8_t i;  
    
      /* ADC Channel used for current reading are read  in order to get zero currents ADC values*/
      //32次采样求平均值,电流传感器初始校准 
     memset((uint8_t*)(&ADCSampPareM1),0,sizeof(ADCSampPareM1));
     memset((uint8_t*)(&ADCSampPareM2),0,sizeof(ADCSampPareM2));
    for(i=32; i!=0; i--)   
      {
    
        ADCSampPareM1.OffsetBUS_Curr += ADC_ConvertedValue[6]; //通道6,总线电流
        ADCSampPareM1.OffsetPhaseV_Curr += ADC_ConvertedValue[4];//电机M1 V相电流
        ADCSampPareM1.OffsetPhaseU_Curr += ADC_ConvertedValue[5];
    
        ADCSampPareM2.OffsetBUS_Curr += ADC_ConvertedValue[7];
        ADCSampPareM2.OffsetPhaseU_Curr += ADC_ConvertedValue[3];
        ADCSampPareM2.OffsetPhaseV_Curr += ADC_ConvertedValue[0];
        Delay(10000);
        Delay(10000);
      }
        ADCSampPareM1.OffsetBUS_Curr = ADCSampPareM1.OffsetBUS_Curr>>5; //除以2^5,获得平均数
        ADCSampPareM1.OffsetPhaseV_Curr= ADCSampPareM1.OffsetPhaseV_Curr>>5; 
        ADCSampPareM1.OffsetPhaseU_Curr=ADCSampPareM1.OffsetPhaseU_Curr>>5;
        ADCSampPareM2.OffsetBUS_Curr = ADCSampPareM2.OffsetBUS_Curr>>5;
        ADCSampPareM2.OffsetPhaseV_Curr= ADCSampPareM2.OffsetPhaseV_Curr>>5; 
        ADCSampPareM2.OffsetPhaseU_Curr=ADCSampPareM2.OffsetPhaseU_Curr>>5;
    }
    

    有了上面的数组,我们可以通过如下的两个函数计算电机相线的电流值

    void   ADC_Sample_M1(void )
    {  
        ADCSampPareM1.PhaseV_Curr=((ADC_ConvertedValue[4]-ADCSampPareM1.OffsetPhaseV_Curr)<<2);     
        ADCSampPareM1.PhaseU_Curr=((ADC_ConvertedValue[5]-ADCSampPareM1.OffsetPhaseU_Curr)<<2);
    }
    
    void   ADC_Sample_M2(void )
    {
        ADCSampPareM2.PhaseV_Curr=((ADC_ConvertedValue[0]-ADCSampPareM2.OffsetPhaseV_Curr)<<2);     
        ADCSampPareM2.PhaseU_Curr=((ADC_ConvertedValue[3]-ADCSampPareM2.OffsetPhaseU_Curr)<<2);
    }
    

    然后调用clark变换和PARK变换,用来做SVPWM计算

            ADC_Sample_M2( );
    
            ClarkeI_M2.As=ADCSampPareM2.PhaseU_Curr;
            ClarkeI_M2.Bs=ADCSampPareM2.PhaseV_Curr;
    

    然后通过下面的计算,获得总线的电流值和温度值,代码定义在AD_period_task中,在user_period_task中被周期性的读取。而电机的电流值读取频率为18KHZ,频率比总线电流和温度的频率要快很多。

      ADCSampPareM1.BUS_Curr  =  ADC_ConvertedValue[6]-ADCSampPareM1.OffsetBUS_Curr;
      ADCSampPareM2.BUS_Curr  =  ADC_ConvertedValue[7]-ADCSampPareM2.OffsetBUS_Curr;        
      dma_ad.bus_volt = VOLT_TO_BUS_VOLT(CAL_ADCDMA_fifo_AVGRPM(AD_DMA_CH3,2)); 
    //  tempRIS_value = VOLT_TO_RISITOR(CAL_ADCDMA_fifo_AVGRPM(AD_DMA_CH2,1));  
      temp_ad100v = CAL_ADCDMA_fifo_AVGRPM(AD_DMA_CH2,1);
      temp_ad100v = adva_to_100v(temp_ad100v);
      tempRIS_value = VOLT_TO_RISI(temp_ad100v);
    
    [ image

    markdownFile.md
    6.7 KB](https://app.yinxiang.com/shard/s51/res/2a638deb-9348-419d-aa4b-38fe1b2851a5/markdownFile.md)

    相关文章

      网友评论

          本文标题:如何构建一块电路板的护城河1_ADC的DMA读取和处理

          本文链接:https://www.haomeiwen.com/subject/qyawjqtx.html