stm32f103zet6HAL库预实现简单的脉冲宽度调制

1.自述:之前看到NE555的芯片手册中有脉冲宽度调制的电路,想着能否用软件方式实现,便用手头的STM32F103RCT6开发板进行尝试。

2..实验器材:stm32f103ZETX开发板

3.大致思路:利用32f1的ADC接收数据,同时利用定时器通道产生PWM波,简单的处理后根据接收到的ADC数据改变PWM的占空比来模拟脉冲宽度调制。

脉冲宽度调制波通常由一列占空比不同的矩形脉冲构成,其占空比与信号的瞬时采样值成比例。如下图芯片手册中的电路所示将正弦信号调制成占空比不同的脉冲信号。

按照芯片手册上的图MUTISIM仿真结果如下:(时基信号为10K的脉冲波,输入信号为400HZ的正弦波,示波器显示的范围貌似不够)

 

 

 

 

 

当然脉冲宽度调制作为一种模拟控制方式,调制方法多样,由于本人水平有限详细了解请自主查资料,这里仅仅用32进行最简单模式的一种尝试。

4.实验过程:

1)CUBEMX配置:

1-1开启时钟,并在时钟树界面将系统时钟设为最大。

 

1-2开启ADC1的通道0用于ADC转换(此处将转换触发条件配置为TIM3的计数溢出,由此可以控制ADC转换的频率)ADC的转换时间其实还与采样时间有关,

但是如果定时器频率不高的话因为ADC_CLK的频率为12MHZ所以会有大约只1us的误差,个人认为可以不计。

 

之后再开启DMA设置,具体设置如下。

 

 

1-3开启定时器TIM3,时钟源选取INTERNAL CLOCK,并将通道1设置为PWM的输出模式。注意要使能auto-reload,并选定触发事件,并开启中断,其他默认即可。(pwmpulse为初设的占空比值,事件占空比为pwmpulse/arr)

 

(中断开启)

 

1-4可以考虑开启串口usart1方便调试,配置默认即可,配置完成后选择保存位置点GENERATE CODE即可,注意路径不要有中文。

 

 2)代码编写:cube的配置代码基本都生成了,我们不用改动,

2-1在Private include中#include "stdio.h",并进行printf的重定向

1 int fputc(int ch, FILE *f){
2 uint8_t temp[1] = {ch};
3 HAL_UART_Transmit(&huart1, temp, 1, 2);//huart1根据配置的串口更改
4 return ch;
5 }

 

2-2在Private variables中加入自定义变量

1 /* USER CODE BEGIN PV */
2 uint32_t adc_buf[NPT]={0};//用于存储ADC数据(NPT之前可自定义,改变数组大小)
3 uint32_t ADCTRUE=0;
4 uint32_t pulse;
5 uint32_t width;
6 uint32_t maxval=0;
7 uint32_t minval=4096;
8 /* USER CODE END PV */

 

2-3在while循环之前开启定时器相应功能

1   HAL_ADCEx_Calibration_Start(&hadc1);//ADC校准
2   HAL_ADC_Start_DMA(&hadc1, adc_buf, NPT);//开启ADCDMA转换
3   HAL_TIM_Base_Start(&htim3);        //开启定时器功能
1   HAL_TIM_Base_Start_IT(&htim3);//开启定时器3中断
2   
3   
4   pulse=(TIM3->ARR)/2;                //脉冲值为TIM3-ARR的1/2
5   HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //开启PWM输出 
6   __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pulse) //设置占空比      

2-4在定时器中断回调函数中进行实时的数据处理与改变脉冲的占空比来实现调制

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    if (htim->Instance == htim3.Instance) {
        for(int i=0;i<NPT;i++){
            ADCTRUE+=adc_buf[i];        
        }
        
        ADCTRUE=ADCTRUE/NPT;              //做一个均值滤波
        if(maxval<ADCTRUE)  maxval=ADCTRUE;     //通过读取ADC的最大最小值来实现设定占空比的范围
        if(minval>ADCTRUE)    minval=ADCTRUE;
        width=(TIM3->ARR)*(ADCTRUE-minval)*1.00/(maxval-minval);//算出应该配置的占空比关联项的数值
        
        //HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_1); 
        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, width);    //设定占空比
        //HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); 
        
        
    }
}
/* USER CODE END 4 */

2-5while循环中可以用串口进行调试,查看数值是否正确

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      printf("adcval=%d width=%d\r\n",ADCTRUE,width);
      HAL_Delay(100);
  }

程序设计完毕,烧录到板子上可以查看效果,本人调试时设置tim3的频率为10KHZ作为脉冲的频率,在示波器上可以观察到输入ADC引脚信号5K左右时脉冲宽呈现输入信号的模式变化,但是频率变大或变小示波器的显示不是很明显。为了观察效果自接了个蜂鸣器,最终发声频率和输入信号的频率还是接近的。

3)不足之处个人认为是不是定时器中断时处理数据占用了一定时间导致设置占空比无法做到接近实时所以难以观察到很明确的频率变化。也可能是定时器产生PWM的同时也占用了中断。32的定时器PWM产生机制和中断机制还没有仔细研究过,有大佬知道望告知。

 

posted @ 2020-09-08 16:41  ffttff  阅读(611)  评论(0)    收藏  举报