使用STM32L4 HAL库通过串口通信调节PWM占空比

STM32 L4 通过串口通信改变PWM占空比 HAL库

使用串行通信的目的是为了让上位机能控制STM32来改变PWM的输出

目录

  • STM32 L4 通过串口通信改变PWM占空比 HAL库
  • 一、PWM初始化
  • 二、串口UART初始化
  • 三、赋值
  • 四、最后附上效果图
  • 一、PWM初始化

    #include "pwm.h"
    
    TIM_HandleTypeDef TIM4_Handler;         //定时器4PWM句柄
    TIM_OC_InitTypeDef TIM4_CHnHandler;	    //定时器4句柄
    
    void TIM4_PWM_Init(u16 arr, u16 psc)
    {
        TIM4_Handler.Instance = TIM4;          				//定时器4
        TIM4_Handler.Init.Prescaler = psc;     				//定时器分频
        TIM4_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
        TIM4_Handler.Init.Period = arr;        				//自动重装载值
        TIM4_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        HAL_TIM_PWM_Init(&TIM4_Handler);       				//初始化PWM
    
        TIM4_CHnHandler.OCMode = TIM_OCMODE_PWM1; 			//模式选择PWM1
        TIM4_CHnHandler.Pulse = arr / 2;       				//设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
        TIM4_CHnHandler.OCPolarity = TIM_OCPOLARITY_HIGH; 	//输出比较极性为低
    
        HAL_TIM_PWM_ConfigChannel(&TIM4_Handler, &TIM4_CHnHandler, TIM_CHANNEL_3); //配置TIM4通道3
        HAL_TIM_PWM_Start(&TIM4_Handler, TIM_CHANNEL_3); 	//开启PWM通道3
    
    }
    
    void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
    {
        GPIO_InitTypeDef GPIO_Initure;
    	__HAL_RCC_TIM4_CLK_ENABLE();				//使能定时器4
        __HAL_RCC_GPIOB_CLK_ENABLE();				//开启GPIOB时钟
    
        GPIO_Initure.Pin = GPIO_PIN_8; //PB8
        GPIO_Initure.Mode = GPIO_MODE_AF_PP;  		//复用推挽输出
        GPIO_Initure.Pull = GPIO_PULLUP;        	//上拉
        GPIO_Initure.Speed = GPIO_SPEED_HIGH;   	//高速
        GPIO_Initure.Alternate = GPIO_AF2_TIM4;		//PB8复用为TIM2_CH1.CH2
        HAL_GPIO_Init(GPIOB, &GPIO_Initure);
    }
    
    void TIM_SetTIM4Compare3(u32 compare)
    {
        TIM4->CCR3 = compare;
    }
    
    

    这里用的是定时器TIM4的3通道,当然也可以改为其他的定时器,具体请参考手册

    二、串口UART初始化

    偷下懒,直接拿正点原子的例子程序修改了一下。
    示例用的是UART1
    引脚是PA9(TX),PA10(RX)

    #include "usart.h"
    #include "delay.h"
    #include <stdio.h> 
    #include <string.h>
    
    extern int mypwm;
    extern u16 pwmval;
    extern u8 chun[10];
    extern int len;
    
    #if 1
    #pragma import(__use_no_semihosting)
    //标准库需要的支持函数
    struct __FILE
    {
        int handle;
    };
    
    FILE __stdout;
    /**
     * @brief	定义_sys_exit()以避免使用半主机模式
     *
     * @param	void
     *
     * @return  void
     */
    void _sys_exit(int x)
    {
        x = x;
    }
    /**
     * @brief	重定义fputc函数
     *
     * @param	ch		输出字符量
     * @param	f		文件指针
     *
     * @return  void
     */
    int fputc(int ch, FILE *f)
    {
        while((USART1->ISR & 0X40) == 0); //循环发送,直到发送完毕
    
        USART1->TDR = (u8) ch;
        return ch;
    }
    #endif
    
    
    #if EN_USART1_RX   //如果使能了接收
    //串口1中断服务程序
    //注意,读取USARTx->SR能避免莫名其妙的错误
    u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    //接收状态
    //bit15,	接收完成标志
    //bit14,	接收到0x0d
    //bit13~0,	接收到的有效字节数目
    u16 USART_RX_STA = 0;     //接收状态标记
    
    UART_HandleTypeDef UART1_Handler; //UART句柄
    
    
    /**
     * @brief	初始化串口1函数
     *
     * @param	bound	串口波特率
     *
     * @return  void
     */
    void uart_init(u32 bound)
    {
        //UART 初始化设置
        UART1_Handler.Instance = USART1;					  //USART1
        UART1_Handler.Init.BaudRate = bound;				  //波特率
        UART1_Handler.Init.WordLength = UART_WORDLENGTH_8B; //字长为8位数据格式
        UART1_Handler.Init.StopBits = UART_STOPBITS_1;	  //一个停止位
        UART1_Handler.Init.Parity = UART_PARITY_NONE;		  //无奇偶校验位
        UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控
        UART1_Handler.Init.Mode = UART_MODE_TX_RX;		  //收发模式
        HAL_UART_Init(&UART1_Handler);					    //HAL_UART_Init()会使能UART1
    
        __HAL_UART_ENABLE_IT(&UART1_Handler, UART_IT_RXNE); //开启接收中断
        HAL_NVIC_EnableIRQ(USART1_IRQn);					//使能USART1中断通道
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);				//抢占优先级3,子优先级3
    		printf("\r\n串口已准备--\r\n");
    }
    
    
    /**
     * @brief	HAL库串口底层初始化,时钟使能,引脚配置,中断配置
     *
     * @param	huart	串口句柄
     *
     * @return  void
     */
    void HAL_UART_MspInit(UART_HandleTypeDef *huart)
    {
        //GPIO端口设置
        GPIO_InitTypeDef GPIO_Initure;
    
        if(huart->Instance == USART1) //如果是串口1,进行串口1 MSP初始化
        {
            __HAL_RCC_GPIOA_CLK_ENABLE();				//使能GPIOA时钟
            __HAL_RCC_USART1_CLK_ENABLE();				//使能USART1时钟
    
            GPIO_Initure.Pin = GPIO_PIN_9;				//PA9
            GPIO_Initure.Mode = GPIO_MODE_AF_PP;		//复用推挽输出
            GPIO_Initure.Pull = GPIO_PULLUP;			//上拉
            GPIO_Initure.Speed = GPIO_SPEED_FAST;		//高速
            GPIO_Initure.Alternate = GPIO_AF7_USART1;	//复用为USART1
            HAL_GPIO_Init(GPIOA, &GPIO_Initure);	   	//初始化PA9
    
            GPIO_Initure.Pin = GPIO_PIN_10;				//PA10
            HAL_GPIO_Init(GPIOA, &GPIO_Initure);	   	//初始化PA10
        }
    
    }
    
    /**
     * @brief	串口1中断服务程序
     *
     * @remark	下面代码我们直接把中断控制逻辑写在中断服务函数内部
     * 			说明:采用HAL库处理逻辑,效率不高。
     *
     * @param   void
     *
     * @return  void
     */
    void USART1_IRQHandler(void)
    {
        u8 Res;
    		int i;
        if((__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_RXNE) != RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
            HAL_UART_Receive(&UART1_Handler, &Res, 1, 1000);
    
            if((USART_RX_STA & 0x8000) == 0) //接收未完成
            {
                if(USART_RX_STA & 0x4000) //接收到了0x0d
                {
                    if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始
    
                    else USART_RX_STA |= 0x8000;	//接收完成了
                }
                else //还没收到0X0D
                {
                    if(Res == 0x0d)USART_RX_STA |= 0x4000;
                    else
                    {
                        USART_RX_BUF[USART_RX_STA] = Res;
                        USART_RX_STA++;
    
                        if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收
                    }
                }
            }
        }
    		HAL_UART_Transmit(&UART1_Handler, &Res, 1, 1000);//发送数据函数,通过USART1通道发送res数据出去
    		if(USART_RX_STA&0x8000)                    
    		{					   
    			len=USART_RX_STA&0x3fff;//计算出接受数据的总长度
    			
    			for(i=0;i<len;i++)
    			{
    				chun[i] = (USART_RX_BUF[i]-0x30);	
    				USART_RX_BUF[i]=0;	
    				//HAL_UART_Transmit(&UART1_Handler, &chun[i], 1, 1000);//发送数据函数,通过USART1通道发送res数据出去
    			}
    			
    			USART_RX_STA = 0; 
    		}
        HAL_UART_IRQHandler(&UART1_Handler);
    }
    
    #endif
    

    主要修改的地方在接收数据的部分,我用len=USART_RX_STA&0x3fff
    计算出串口接收到数据的长度,然后把接收缓存USART_RX_BUF中的每一位逐个放到chun[ ]数组中。
    由于pc端每次发送的是字符类型的数据,例如‘1’对应的ASCII码是0x31,0对应的是0x30。那么对于输入的数字每个都要减去0x30就会得到 01,02,03.。。。

    三、赋值

    初始化都处理完了之后就准备在主函数进行赋值了

    #include "sys.h"
    #include "usart.h"
    #include "delay.h"
    #include "pwm.h"
    
    #define ARR1 1000
    #define PSC1 80
    
    int mypwm=0;
    u16 pwmval = 0;			
    u8 chun[10];
    int len=0;
    
    int main(void)
    {
        HAL_Init();
        SystemClock_Config();		//初始化系统时钟为80M
        delay_init(80); 			//初始化延时函数    80M系统时钟
        uart_init(115200);			//初始化串口,波特率为115200
    
    	TIM4_PWM_Init(ARR1 - 1, PSC1 - 1);//TIM2时钟频率 80M/80=1M   计数频率1M/1000=1KHZ     默认占空比为50%
    	pwmval=0;
    		
        while(1)
        {
    			if(len==2)
    			{
    				mypwm =((int)chun[0]*10 + (int)chun[1]);
    			}
    			else if(len==3)
    			{
    				mypwm =((int)chun[0]*10*10 + (int)chun[1]*10 + (int)chun[2]);
    				if(mypwm>100)
    				{
    					printf("\r\n占空比不能超过100%\r\n");
    					mypwm = 0;
    					len=0;
    				}
    			}
    			else if(len==1)
    			{
    				mypwm =((int)chun[0]);
    			}
    			else if(len>3)
    			{
    				printf("\r\n请输入长度 < 3的字符\r\n");
    				printf("\r\n占空比最大为100,不用输入百分号\r\n");
    				len=0;
    			}			
    			pwmval = ARR1*(mypwm*1.0/100);//计算占空比并赋值
    			if(pwmval>ARR1)pwmval=ARR1;//限幅
    			TIM_SetTIM4Compare3(pwmval);//输出PWM
        }
    }
    

    四、最后附上效果图

    占空比为30%

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32L4 HAL库通过串口通信调节PWM占空比

    发表评论