本程序是在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

#include

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

{

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

{

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

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)的作业、课设、毕业设计皆可询问本人。

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: