使用STM32C8T6控制智能小车的代码实现

本程序是在keil5软件下使用标准库编程的,纯自己手打,包含详细的代码注释。

首先就是PWM模块,次模块用于改变小车的转速,本项目用一个L98N电机驱动模块,将左边的两个轮子连接在一个L98N电机驱动模块输出引脚,将坐标的两个轮子连接在L298N的另外一个输出引脚,由此控制四个轮子的转动。

下面是PWM模块的代码

PWM.c

#include "stm32f10x.h"                  // Device header

//用两个定时器产生PWM波形,分别控制两侧电机
//PA6、PA7、PA8、PA9四个引脚分别产生四个PWM波形控制左轮前进调速、左轮后退调速、右轮前进调速、右轮后退调速
//其中轮子的前进和后退输出的PWM波形必须相同

//初始化TIM1时钟,控制PA8、PA9上的电机,也就是右侧电机
void TIM1_PWM_Init(void)
{
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//配置时基单元
	TIM_InternalClockConfig(TIM1);
	//配置时基单元//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//ARR//自动重装值,当到100时执行一次中断
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//PSC//将72HMZ分为720份
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
	
	
	//配置输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//给结构体变量赋初始值
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出比较极性
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
	TIM_OCInitStruct.TIM_Pulse=0;//设置CCR
	//ARR=100-1,PSC=720-1,CCR=50。此时就是频率为1KHz,占空比为50%的PWM波形
	TIM_OC1Init(TIM1,&TIM_OCInitStruct);
	TIM_OC2Init(TIM1,&TIM_OCInitStruct);
	TIM_ARRPreloadConfig(TIM1,ENABLE);								   // 使能TIM1的自动重装载寄存器
	TIM_CtrlPWMOutputs(TIM1,ENABLE);								// 主输出使能
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  				// 使能TIM1在OC1上的预装载寄存器
	TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);				// 使能TIM1在OC2上的预装载寄存器	
	//运行控制,启动计数器
	TIM_Cmd(TIM1,ENABLE);
}


//初始化TIM3时钟,控制PA6、PA7上的电机,也就是左侧电机
void TIM3_PWM_Init(void)
{
	//RCC开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							
	//配置时基单元
	TIM_InternalClockConfig(TIM3);
	//配置时基单元//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//ARR//自动重装值,当到100时执行一次中断
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//PSC//将72HMZ分为720份
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	
	//配置输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//给结构体变量赋初始值
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出比较极性
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
	TIM_OCInitStruct.TIM_Pulse=0;//设置CCR
	//ARR=100-1,PSC=720-1,CCR=50。此时就是频率为1KHz,占空比为50%的PWM波形
	TIM_OC1Init(TIM3,&TIM_OCInitStruct);
	TIM_OC2Init(TIM3,&TIM_OCInitStruct);
	TIM_ARRPreloadConfig(TIM3,ENABLE);								   // 使能TIM1的自动重装载寄存器
	TIM_CtrlPWMOutputs(TIM3,ENABLE);								// 主输出使能
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  				// 使能TIM1在OC1上的预装载寄存器
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);				// 使能TIM1在OC2上的预装载寄存器	
	//运行控制,启动计数器
	TIM_Cmd(TIM3,ENABLE);
}

PWM.h

#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" 

void TIM1_PWM_Init(void);//初始化TIM1时钟,控制PA8、PA9上的电机,也就是右侧电机
void TIM3_PWM_Init(void);//初始化TIM3时钟,控制PA6、PA7上的电机,也就是左侧电机


#endif

接下来是电机控制的代码

Motor.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

//电机初始化
void Motor_Init(void)
{
	TIM1_PWM_Init();
	TIM3_PWM_Init();
}

//设置左轮的速度
void Motor_SetLeftSpeed(int8_t Speed)
{
	if(Speed>0)//前进
	{	
		GPIO_SetBits(GPIOA,GPIO_Pin_6);//产生PWM波形
		GPIO_ResetBits(GPIOA,GPIO_Pin_7);
		TIM_SetCompare1(TIM3,Speed);//PA6连接TIM3_CH1通道
	}
	else if(Speed==0)//停止
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_7);
		GPIO_SetBits(GPIOA,GPIO_Pin_6);
	}
	else //后退
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_7);//产生PWM波形
		GPIO_ResetBits(GPIOA,GPIO_Pin_6);
		TIM_SetCompare2(TIM3,-Speed);//PA7连接TIM3_CH2通道
	}
}

//设置右轮的速度
void Motor_SetRightSpeed(int8_t Speed)
{
	if(Speed>0)//前进
	{	
		GPIO_SetBits(GPIOA,GPIO_Pin_8);//产生PWM波形
		GPIO_ResetBits(GPIOA,GPIO_Pin_9);
		TIM_SetCompare1(TIM1,Speed);//PA8连接TIM1_CH1通道
	}
	else if(Speed==0)//停止
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_8);
		GPIO_SetBits(GPIOA,GPIO_Pin_9);
	}
	else //后退
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_9);//产生PWM波形
		GPIO_ResetBits(GPIOA,GPIO_Pin_8);
		TIM_SetCompare2(TIM1,-Speed);//PA9连接TIM1_CH2通道
	}
}

Motor.h

#ifndef __MOTOR_H
#define __MOTOR_H
#include "stm32f10x.h" 

void Motor_Init(void);//电机初始化
void Motor_SetLeftSpeed(int8_t Speed);//设置左轮的速度//Speed>0前进,<0后退
void Motor_SetRightSpeed(int8_t Speed);//设置右轮的速度//Speed>0前进,<0后退

#endif

接下来是控制小车模块的代码

Car.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "Motor.h"
#include "Delay.h"

//小车初始化
void Car_Init(void)
{
	Motor_Init();
}

//前进调速
void Go_Ahead(int8_t Speed)
{
	Motor_SetLeftSpeed(-Speed);
	Motor_SetRightSpeed(-Speed);
}

//后退调速
void Go_Back(int8_t Speed)
{
	Motor_SetLeftSpeed(Speed);
	Motor_SetRightSpeed(Speed);
}

//右转
void Turn_Left(void)
{
	Motor_SetLeftSpeed(-40);
	Motor_SetRightSpeed(80);
}

//左转
void Turn_Right(void)
{
	Motor_SetLeftSpeed(80);
	Motor_SetRightSpeed(-40);
}

//停止
void Car_stop(void)
{
	Motor_SetLeftSpeed(0);
	Motor_SetRightSpeed(0);
}

Car.h

#ifndef __CAR_H
#define __CAR_H
#include "stm32f10x.h" 

void Car_Init(void);//小车初始化
void Go_Ahead(int8_t Speed);//前进调速//Speed>0前进,<0后退
void Go_Back(int8_t Speed);//后退调速//Speed>0前进,<0后退
void Turn_Left(void);//右转
void Turn_Right(void);//左转
void Car_stop(void);//停止

#endif

本项目可以使用蓝牙控制小车的前进后退转弯等,接下来是蓝牙模块的代码

lanya.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RXData;//定义一个变量,用于存储接收的数据
uint8_t Serila_RXFlag;//接收变量标志位


void Serial_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//初始化PA9引脚,用于串口发送//32的发送端//电脑的接收端
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//初始化PA10引脚,用于串口接收//32的接收端//电脑的发送端
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//配置USART
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate=9600;//波特率
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_RTS;//硬件流控制//不需要控制流
	USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;//发送+接收模式
	USART_InitStruct.USART_Parity=USART_Parity_No;//不需要校验位
	USART_InitStruct.USART_StopBits=USART_StopBits_1;//停止位//1位停止位
	USART_InitStruct.USART_WordLength=USART_WordLength_8b;//选择八位字长
	USART_Init(USART2,&USART_InitStruct);
	//开启中断
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//初始化NVIC的USART1通道
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=USART2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	//开启USART
	USART_Cmd(USART2,ENABLE);
}

//发送字节
void Seral_SendByte(uint8_t Byte)
{
	USART_SendData(USART2,Byte);//将串口的数据发送到电脑上
	while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待传输完成
}

//发送数组
void Seril_SendArray(uint8_t *Array,uint16_t Length)
{
	uint16_t i;
	for(i=0;i<Length;i++)
	{
		Seral_SendByte(Array[i]);//通过for循环一位一位的发送数组中的数据
	}
}

//发送字符串
void Seril_SendString(char *String)
{
	uint8_t i;
	for(i=0;String[i]!='\0';i++)
	{
		Seral_SendByte(String[i]);//通过for循环一位一位的发送数组中的数据
	}
}

uint32_t Seril_Pow(uint32_t X,uint32_t Y)
{
	uint32_t Result=1;
	while(Y--)
	{
		Result*=X;
	}
	return Result;
}

//发送数字
void Seril_SendNumber(uint32_t Number,uint8_t Length)
{
	uint8_t i;
	for(i=0;i<Length;i++)
	{
		Seral_SendByte(Number/Seril_Pow(10,Length-i-1)%10+'0');
	}
}

int fputc(int ch,FILE *f)//将fputc函数重定向到串口
{
	Seral_SendByte(ch);
	return ch;
}

//打印
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Seril_SendString(String);
}


//清除标志位函数,自动清除标志位
uint8_t Serial_GetRxFlag(void)
{
	if(Serila_RXFlag==1)//如果接收标志位为1,则清除接收标志位,并且函数返回1
	{
		Serila_RXFlag=0;
		return 1;
	}
	return 0;
}

//这个函数用于返回接收到的数据
uint8_t Serial_GetRxData(void)
{
	return Serial_RXData;
}

//中断函数
void USART2_IRQHandler(void)
{
	if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET)//如果这个函数为1,说明接收数据的发生
	{
		Serial_RXData=USART_ReceiveData(USART2);//将数据存储在变量Serial_RXData中
		Serila_RXFlag=1;//接收标志位置1
		USART_ClearITPendingBit(USART2,USART_IT_RXNE);//清除接收标志位
	}
}

lanya.h

#ifndef __LANYA_H
#define __LANYA_H
#include "stm32f10x.h"
#include <stdio.h>

void Serial_Init(void);
void Seral_SendByte(uint8_t Byte);
void Seril_SendArray(uint8_t *Array,uint16_t Length);
void Seril_SendString(char *String);
void Seril_SendNumber(uint32_t Number,uint8_t Length);
void Serial_Printf(char *format, ...);
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);


#endif

最后就是主函数了

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Car.h"
#include "lanya.h"
#include "OLED.h"



uint8_t Date;//定义一个变量,用于存储接收的数据、
uint8_t Go_Ahead_flag=0;//0表示没有前进,1前进50,2前进100
uint8_t Go_Back_flag=0;//0表示没有前进,1前进50,2前进100

int main(void)
{
	Car_Init();//初始化小车
	Serial_Init();//初始化蓝牙串口
	OLED_Init();//初始化OLED
	
	OLED_ShowString(1,4,"Car control");
	OLED_ShowString(2,1,"Data:");
	OLED_ShowString(3,1,"Command:");
	OLED_ShowString(4,1,"Speed:");
	
	while (1)
	{		
		if(Serial_GetRxFlag()==1)//如果Serial_GetRxFlag()返回值是1说明有数据接收的发生
		{
			Date=Serial_GetRxData();//将接收到的数据传到变量RxDate中
			OLED_ShowHexNum(2,9,Date,2);//在OLED上显示接收到的数据
			
			if(Date==02)//前进
			{
				Car_Init();
				Seril_SendString("Go_Ahead");//将对应的命令返回到手机上
				OLED_ShowString(3,9,"Go_Ahead");//将对应的命令在怕屏幕上显示
				OLED_ShowNum(4,9,50,2);
				Go_Ahead(100);//前进50
			}	
			if(Date==00)//后退
			{
				Car_Init();
				Seril_SendString("Go_Back");//将对应的命令返回到手机上
				OLED_ShowString(3,9,"Go_Back");//将对应的命令在怕屏幕上显示
				OLED_ShowNum(4,9,50,2);
				Go_Back(50);//后退50
			}
			if(Date==06)//右转
			{
				Car_Init();
				Seril_SendString("TurnRight");//将对应的命令返回到手机上
				OLED_ShowString(3,9,"TurnRight");//将对应的命令在怕屏幕上显示
				Turn_Right();
				OLED_ShowNum(4,9,00,2);
			}	
			if(Date==04)//左转
			{
				Car_Init();
				Seril_SendString("TurnLeft");//将对应的命令返回到手机上
				OLED_ShowString(3,9,"TurnLeft");//将对应的命令在怕屏幕上显示
				Turn_Left();
				OLED_ShowNum(4,9,00,2);
			}
			if(Date==07)//停止
			{
				Car_Init();
				Seril_SendString("Car_stop");//将对应的命令返回到手机上
				OLED_ShowString(3,9,"Car_stop");//将对应的命令在怕屏幕上显示
				Car_stop();//停止
				OLED_ShowNum(4,9,00,2);
			}
		}
	}
}

使用蓝牙调试软件(各大手机应用商店都有下载),连接蓝牙模块后,想蓝牙模块发送相应代码,小车就可以执行相应的命令。

到此本项目就暂时分享完了,如果需要完成的工程软件评论我,我发给你。

PS:本人熟悉51单片机与STM32的操作与使用,如有关于单片机(51、STM32)的作业、课设、毕业设计皆可询问本人。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32C8T6控制智能小车的代码实现

发表评论