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

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

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

一、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%在这里插入图片描述
在这里插入图片描述

  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值