STM32+DMA串口+RC信号控制舵机


前言

给学校团队成员培训的内容
YC YC


一、RC遥控器PWM信号

根据航模标准,PWM信号线的频率应该是50Hz,对应的每个周期总时长是20ms,输出到舵机的油门线(控制线,也就是细细的,除了红的是接5V电源,黑的GND,另外那个就是数据线)。
这是航模无线遥控模型的一个标准。信号的有效值与1ms~ 2ms的脉宽有关,与脉冲重复率无关。1~ 2ms的方波脉宽渐变过程对应信号的从小到大的渐变。 脉宽的幅度2.5V~ 6V;所以3~5V工作电压的单片机都适用。
这个PWM信号标准不仅对航模舵机适用对航模电调、飞控同样适用。

二、关于舵机

舵机知识

三、STM32串口DMA

串口DMA

四、CUBEMX配置设置

用定时器2输出PWM信号,其配置如下图
定时器配置

使用串口1作为控制串口,其配置如下图中
串口配置
串口DMA配置
在NVIC配置中打开串口1的中断
NVIC

五、串口+DMA通讯

在usart.h文件适当位置添加下列代码

#include "stdio.h"

#define U1_BUFFER_SIZE 200 //定义最长接收字节
//定义u1_printf功能,兼容我们平时的使用习惯,功能与printf相似	                       
#define u1_printf(...)    HAL_UART_Transmit_DMA(&huart1,\
										      (uint8_t *)u1_printf_buf,\
										      sprintf((char*)u1_printf_buf,__VA_ARGS__))
	
extern uint8_t u1_rx_buf[U1_BUFFER_SIZE];//接收缓存
extern volatile uint8_t u1_rx_len;//接收长度
extern volatile uint8_t u1_recv_end_flag;//接收完成标志
extern uint8_t u1_printf_buf[U1_BUFFER_SIZE];//发送缓存

在usart.c文件适当位置添加下列代码


uint8_t u1_rx_buf[U1_BUFFER_SIZE];
volatile uint8_t u1_rx_len=0;
volatile uint8_t u1_recv_end_flag=0;
uint8_t u1_printf_buf[U1_BUFFER_SIZE];
	
  /* USER CODE BEGIN USART1_Init 2 */
	
	/*STM32的IDLE的中断产生条件:
	在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有
	接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到
	数据,即产生IDLE中断
	*/
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能idle中断
	HAL_UART_Receive_DMA(&huart1,u1_rx_buf,U1_BUFFER_SIZE);//打开DMA接收,数据存入rx_buffer数组中
  /* USER CODE END USART1_Init 2 */

在stm32f1xx_it.c文件中的串口中断函数修改如下

#include "usart.h"

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET))//idle标志被置位
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		temp = huart1.Instance->SR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
		temp = huart1.Instance->DR; //读取数据寄存器中的数据
		HAL_UART_DMAStop(&huart1); //
		temp  = hdma_usart1_rx.Instance->CNDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面
		u1_rx_len =  U1_BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		u1_recv_end_flag = 1;	// 接受完成标志位置1	
	 }
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

之后便可以在引用了 usart.h 的文件下则可以通过判断u1_recv_end_flag标志位来知道是否完成串口数据接收,并可以调用u1_printf函数通过串口1发送数据。
例子如下

if(u1_recv_end_flag ==1)
	{
		u1_printf("rx_len=%d\r\n%s\r\n ",u1_rx_len,u1_rx_buf);//打印接收长度与接收数据
		for(uint8_t i=0;i<u1_rx_len;i++)
			{
				u1_rx_buf[i]=0;//清接收缓存
			}
		u1_rx_len=0;//清除计数
		u1_recv_end_flag=0;//清除接收结束标志位
		HAL_UART_Receive_DMA(&huart1,u1_rx_buf,U1_BUFFER_SIZE);//重新打开DMA接收
	}

六、PWM输出

HAL_TIM_PWM_Start 使能PWM输出

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);

__HAL_TIM_SetCompare 函数修改CCR,修改比较值,修改占空比

__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 100); //控制占空比

七、串口控制舵机

main函数中while代码


  int angle = 0;
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(u1_recv_end_flag ==1)
		{
			u1_printf("rx_len=%d\r\n%s\r\n ",u1_rx_len,u1_rx_buf);//打印接收长度与接收数据
			sscanf((char *)u1_rx_buf,"%d\r\n",&angle);
			
			if(angle>100)angle = 100;
			else if(angle<0)angle = 0;
			
			__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 1000 + angle * 10); //控制占空比
			 
			memset(u1_rx_buf,0,200);
			u1_rx_len=0;//清除计数
			u1_recv_end_flag=0;//清除接收结束标志位
			HAL_UART_Receive_DMA(&huart1,u1_rx_buf,U1_BUFFER_SIZE);//重新打开DMA接收
		}
    /* USER CODE END WHILE */

八、通讯测试

串口通讯
向串口发送0-100的数字 串口回复符合预期 通讯成功 注意选择发送新行

九、PWM输出测试

在这里插入图片描述
将PWM的输出口PA0和GND连接到示波器表笔的两端 上电观察示波器
在这里插入图片描述
示波器可以看到PA0脚的对地电压波形为PWM方波,频率为50Hz,高电平持续时间1.5ms
在这里插入图片描述
通过串口向单片机发送100,可以看到PWM波形的高电平持续时间变成2ms
在这里插入图片描述
同样通过串口向单片机发送100,可以看到PWM波形的高电平持续时间变成1ms
至此PWM输出测试完成

十、舵机测试

在这里插入图片描述
先对舵机进行接线黄色为信号线 红色为5V电源线 棕色为GND
完成接线后对系统进行上电,可以看到舵机旋转动作
通过串口向单片机发送控制指令,舵机也会有相应的运动动作。


总结

实验成功,加油诶!

  • 3
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用STM32F103RC微控制器来控制舵机。首先,确保你的舵机可以工作在STM32F103RC的电压范围内(通常为5V)。然后,连接舵机信号线到STM32F103RC的GPIO引脚。 接下来,你需要编写代码来控制舵机。你可以使用STM32CubeIDE或者其他类似的开发环境来编写代码。以下是一个简单的示例代码来控制舵机的角度: ```c#include "stm32f1xx_hal.h" TIM_HandleTypeDef htim; void TIM_Config(void); void TIM_SetAngle(uint16_t angle); int main(void) { HAL_Init(); TIM_Config(); while (1) { // 设置舵机角度为0度 TIM_SetAngle(0); HAL_Delay(1000); // 设置舵机角度为90度 TIM_SetAngle(90); HAL_Delay(1000); // 设置舵机角度为180度 TIM_SetAngle(180); HAL_Delay(1000); } } void TIM_Config(void) { TIM_OC_InitTypeDef sConfigOC; // 启用定时器时钟 __HAL_RCC_TIM2_CLK_ENABLE(); // 初始化定时器 htim.Instance = TIM2; htim.Init.Prescaler =72 -1; // 设置预分频值为72 htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period =20000 -1; // 设置计数器周期为20000 htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim); // 配置定时器通道1为PWM输出模式 sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse =1500; // 设置初始占空比为50%(对应90度) sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1); // 启动定时器 HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); } void TIM_SetAngle(uint16_t angle) { // 计算占空比 uint16_t pulse = (angle *1000 /180) +1000; // 将角度映射到1000-2000的脉宽范围 // 设置占空比 TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = pulse; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1); } ``` 这段代码使用了STM32F103RC的定时器2和通道1来生成PWM信号控制舵机。通过调整`TIM_SetAngle()`函数中的`angle`参数,你可以改变舵机的角度。注意,你需要根据你的舵机型号和需求调整计算占空比的公式。 请确保在使用舵机之前,你已经正确配置STM32F103RC的时钟和GPIO引脚。另外,确保你已经正确连接了舵机的电源和地线。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值