本程序是在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);
}
}
}
}
使用蓝牙调试软件(各大手机应用商店都有下载),连接蓝牙模块后,想蓝牙模块发送相应代码,小车就可以执行相应的命令。
到此本项目就暂时分享完了,如果需要完成的工程软件评论我,我发给你。文章来源:https://www.toymoban.com/news/detail-754006.html
PS:本人熟悉51单片机与STM32的操作与使用,如有关于单片机(51、STM32)的作业、课设、毕业设计皆可询问本人。文章来源地址https://www.toymoban.com/news/detail-754006.html
到了这里,关于STM32C8T6控制智能小车代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!