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.。。。
三、赋值
初始化都处理完了之后就准备在主函数进行赋值了文章来源:https://www.toymoban.com/news/detail-609873.html
#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%
文章来源地址https://www.toymoban.com/news/detail-609873.html
到了这里,关于STM32L4 HAL库通过串口通信改变PWM占空比的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!