基于RT-Thread的遥控小车

        最近学习了RT-Thread操作系统,学习了其中的内核,包括线程调度,时钟管理,线程通信等,理解了其中的运行机制,最大的优点就是可以使线程并行,实时性高,大大提高运行效率。在学习完后,觉得用RT-Thread做一个项目。

项目介绍:

这个小车之前用裸机跑过程序,于是就在此代码的基础上进行修改

        以下是裸机的主函数代码,可以看出所有的任务都在一个线程里面,包括通信,驱动电机,驱动舵机,并且其中还加了些延迟,程序运行效率不高。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Timer.h"
#include "Serial.h"
#include "Motor.h"
#include "Servo.h"

#define   UNIT_PWM	3

int main(void)
{
	u8 flat,RxData;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	Timer_Init(71,9);
	Serial_Init();
	Motor_Init();
	Servo_Init();
	
	while(1)
	{						
			if(Serial_GetRxFlag() == 1)
			{
				RxData = Serial_GetRxData();
				
				//遥控
				if(RxData == 'A')
					flat = 1;	
			}
			
			//遥控
			if(flat == 1)
			{
				if(RxData == 'D')//直线
				{
					Car_Forward(80,80);	
				}
				else if(RxData == 'E')//左转
				{
					Car_TurnLeft(0,80);
				}					
				else if(RxData == 'F')//右转
				{
					Car_TurnRight(80,0);	
				}									
				else if(RxData == 'G')//倒车
				{
					Car_Backward(-80,-80);
				}					
				else if(RxData == 'O')//旋转
				{
					Car_Turnaround(80,-80);
				}						
				else if(RxData == 'H')
				{
					Set_Up_Servo_Forward(UNIT_PWM);
				}					
				else if(RxData == 'I')
				{
					Set_Up_Servo_Backward(UNIT_PWM);
				}						
				else if(RxData == 'J')
				{
					Set_Below_Servo_Forward(UNIT_PWM);
				}						
				else if(RxData == 'K')
				{
					Set_Below_Servo_Backward(UNIT_PWM);
				}						
				else if(RxData == 'S')
				{
					Car_Stop();
				}						
			}
			Delay_ms(20);//避免频繁查询按键信息,防止返回过来的信息还没有完全告知完的时候, MCU又发起了新一轮的查询
	}	
}

因为这个小车有驱动,有舵机,有通信,所以准备分成3个线程

首先是三个线程的创建

	//创建电机驱动的线程
	th_motor = rt_thread_create("th_motor",th_motor_entry,RT_NULL,512,2,1000);
	if(th_motor == RT_NULL)
	{
		rt_kprintf("th_motor create failed...\n");
		return -RT_ENOMEM;
	}
	else
	{
		rt_kprintf("th_motor create successfully...\n");
		rt_thread_startup(th_motor);   //开启电机驱动的线程
	}
		
	//创建舵机云台的线程
	th_servo = rt_thread_create("th_servo",th_servo_entry,RT_NULL,512,2,1000);
	if(th_servo == RT_NULL)
	{
		rt_kprintf("th_servo create failed...\n");
		return -RT_ENOMEM;
	}
	else
	{
		rt_kprintf("th_servo create successfully...\n");
		rt_thread_startup(th_servo);   //开启舵机云台的线程
	}
		
	//创建USART的线程
	th_serial = rt_thread_create("th_serial",th_serial_entry,RT_NULL,512,1,500);
	if(th_serial == RT_NULL)
	{
		rt_kprintf("th_serial create failed...\n");
		return -RT_ENOMEM;
	}
	else
	{
		rt_kprintf("th_serial create successfully...\n");
		rt_thread_startup(th_serial);  //开启USART的线程
	}

在运行了线程的创建函数后,程序会开始运行线程函数,该函数即rt_thread_create()的第二个参数,在该函数内执行相应的动作。

//电机驱动线程
void th_motor_entry(void *parameter)
{	
	while(1)
	{
		if(rt_mb_recv(mail,(rt_uint32_t *)&RxData,RT_WAITING_FOREVER) == RT_EOK)//rt_mb_recv()函数的第二个参数是rt_uint32_t*类型的指针
		{
			rt_kprintf("rt_mb_recv successfully! the value is %c\n",*RxData);//str是一个指向字符的指针,而*str表示该指针指向的字符。如果去掉*则输出的是str指针的地址,而不是指向的具体字符
		}
		
		//遥控
		if(*RxData == 'A')
		{
			flat = 1;
			rt_kprintf("enter control mode\n");
		}
		
		if(flat == 1)
		{			
			if(*RxData == 'D')//直线
			{
				Car_Forward(80,80);	
			}
			else if(*RxData == 'E')//左转
			{
				Car_TurnLeft(0,80);
			}					
			else if(*RxData == 'F')//右转
			{
				Car_TurnRight(80,0);
			}									
			else if(*RxData == 'G')//倒车
			{
				Car_Backward(-80,-80);
			}					
			else if(*RxData == 'O')//旋转
			{
				Car_Turnaround(80,-80);
			}
			else if(*RxData == 'S')
			{
				Car_Stop();
			}
		}			
		rt_thread_delay(1000);
	}
}

//舵机云台线程
void th_servo_entry(void *parameter)
{
	while(1)
	{
		if(flat == 1)
		{
			if(*RxData == 'H')
			{
				Set_Up_Servo_Forward(UNIT_PWM);
			}					
			else if(*RxData == 'I')
			{
				Set_Up_Servo_Backward(UNIT_PWM);
			}						
			else if(*RxData == 'J')
			{
				Set_Below_Servo_Forward(UNIT_PWM);
			}						
			else if(*RxData == 'K')
			{
				Set_Below_Servo_Backward(UNIT_PWM);
			}
		}
		rt_thread_delay(1500);
	}	
}

//USART线程
void th_serial_entry(void *parameter)
{
	u8 str;
	
	while(1)
	{
		if(Serial_GetRxFlag() == 1)
		{
			str = Serial_GetRxData();
			if(rt_mb_send(mail,(rt_uint32_t)&str) == RT_EOK)  //rt_mb_send()函数的第二个参数是rt_uint32_t类型的数据,如果去掉地址符号 &,那么传递给 rt_mb_send() 函数的就是一个字面量字符 ‘A’,而不是 mb_str1 所在的地址了
			{
				rt_kprintf("rt_mb_send successfully!\n");
			}
			else
			{
				rt_kprintf("rt_mb_send failed...\n");
			}					
		}
		rt_thread_delay(1000);
	}
}

三个线程每n*tick(n为rt_thread_create()函数的最后一个参数)时间内会切换一次,由于切换的速度非常快,所以可以看作三个线程在同时运行。

在其中也加入了邮箱,用于线程之间的通信

	//创建动态邮箱
	mail = rt_mb_create("mail",32,RT_IPC_FLAG_FIFO);
	if(mail == RT_NULL)
	{
		rt_kprintf("mail create failed...\n");
		return -RT_ENOMEM;
	}
	else
	{
		rt_kprintf("mail create successfully...\n");
	}

通过rt_mb_send()函数和rt_mb_recv()函数来进行邮件的收发,发送和接收的数据是4字节32位的整形数据,发送时要发送数据的地址,接收时通过指针来解引用

以上就是该项目的部分内容,因全写在主函数不太美观,所以我把线程的创建和调度用在了各个.c文件,以下是修改后的主函数代码,非常的简洁明了,主函数中仅创建了3个线程即可实现全部的功能,由此可见RT_Thread的强大。

#include "stm32f10x.h"
#include "Delay.h"
#include "Timer.h"
#include "Serial.h"
#include "Motor.h"
#include "Servo.h"

#include "rtthread.h"
#include "rthw.h"

int main(void)
{
	mb_Init();     //邮箱初始化
	Motor_Init();	 //电机驱动初始化
	Servo_Init();  //舵机云台初始化
}

效果展示

代码链接

链接:https://pan.baidu.com/s/1NpKJyW4xF8Wk1-7WtKE3rw?pwd=oxon 
提取码:oxon 

项目所遇问题(已解决)

  1. .h文件声明错误,编译下载能通过,但会导致功能无法实现
  2. 引脚重复定义导致功能无法实现
  3. PWM(定时器通道)、USART、IIC、SPI等引脚的是在单片机上定义好了的,不能随便配置
  4. 机械臂的舵机设置了几个参数,如pwm,middle,left_limit,因为这几个参数都属于舵机,所以可以用一个结构体封装起来,然后直接引用成员变量即可,又因为有多个舵机,所以再用这个结构体类型定义几个对象,每个对象内的结构体成员参数都不一样
  5. 舵机如果电压给大了,可能会出现一直抖动的情况
  6. 使用任何的GPIO口前都要进行初始化
  7. 串口初始化要放在rt_hw_board_init()函数里才能使用rt_kprintf
  8. 修改时钟节拍在rtconfig.h文件里22行RT_TICK_PER_SECOND,1tick = 1/RT_TICK_PER_SECOND秒
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值