1.介绍
随着机械、电子、控制、信息等技术的飞速发展在其他技术方面,智能机器人有着广阔的应用前景。今天,我们希望通过追踪车走进智能之门。
在本次实验中,我们希望将所学的C语言知识加以应用,结合起来用探索的stm32单片机来完成对电机进行控制,使小车能够完成可变要求速度驾驶和转弯。
1.1目的
只有应用我们所学到的知识,才能使知识发挥其价值和作用同时加深了我们对知识的理解。从这里开始重点,希望通过car项目真正应用C语言。而且在完成项目的过程中,你会接触到很多你想要的那些不明白的东西。这样,他们可以学习和应用新的知识与更高以目标为导向的方式提高效率。最重要的是我们会在实验中遇到各种困难,提高自己解决问题的能力
问题也是我们实验的重要意义。
1.2假设
在实验中,我们将学习如何用C语言编写程序,如何使用Keil烧录程序,了解PWM的原理,以及单片机如何实现微机控制PWM输出,控制电机转速。最后,我们将使汽车前进,后退,并根据需要转几秒钟并重复多次。您也可以将速度值设为变量随时改变。
理论
PWM,全称为脉冲宽度调制.PWM是一种数字化的方法对模拟信号电平进行编码。通过使用高分辨率计数器方波的占空比被调制以编码特定模拟的电平信号。
PWM有两个重要参数:频率和占空比。频率调制可以通过改变每单位时间的脉冲数来实现;电压调制可以通过改变占空比来实现。占空比越大,获得的平均电压越大,振幅也越大占空比越小,获得的平均电压越小,电压越低振幅。
在图中,我们假设定时器在递增计数PWM模式下工作,并且
当CNT<CCRx时,输出0,当CNT>=CCRx时,输出1。那你呢可以得到以上PWM原理图:当CNT值小于CCRx,当CNT值大于或等于时,IO输出低电平(0)至CCRx。此时,IO输出高电平(1)。当CNT到达ARR值,它重置为零,然后再次计数,依次循环。更改CCRx的值,可以改变PWM输出的占空比,并改变ARR的值,你可以改变PWM。输出频率是原理脉宽调制输出占空比(D)的公式为:
D = T 1 / ( T 1 + T 2 ) D = T1/(T1+T2) D=T1/(T1+T2)
代码示例
motor.c
#include "motor.h"
#include "interface.h"
#include "stm32f10x.h"
//GPIO配置函数
void MotorGPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = LEFT_F_PIN | LEFT_B_PIN | RIGHT_F_PIN | RIGHT_B_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LEFT_F_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LEFT_B_PIN;
GPIO_Init(LEFT_B_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = RIGHT_F_PIN;
GPIO_Init(RIGHT_F_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = RIGHT_B_PIN;
GPIO_Init(RIGHT_B_GPIO, &GPIO_InitStructure);
}
uint16_t p;
//根据占空比驱动电机转动
void CarMove(void)
{
//左轮
if(left_speed_duty > 0)//向前
{
if(speed_count < left_speed_duty)
{
LEFT_GO;
}else
{
LEFT_STOP;
}
}
else if(left_speed_duty < 0)//向后
{
if(speed_count < (-1)*left_speed_duty)
{
LEFT_BACK;
}else
{
LEFT_STOP;
}
}
else //停止
{
LEFT_STOP;
}
//右轮
if(right_speed_duty > 0)//向前
{
if(speed_count < right_speed_duty)
{
RIGHT_GO;
}else //停止
{
RIGHT_STOP;
}
}
else if(right_speed_duty < 0)//向后
{
if(speed_count < (-1)*right_speed_duty)
{
RIGHT_BACK;
}else //停止
{
RIGHT_STOP;
}
}
else //停止
{
RIGHT_STOP;
}
}
//向前
void CarGo(void)
{
left_speed_duty=MAX_SPEED_DUTY;
right_speed_duty=X_SPEED_DUTY;
}
//向左
void CarLeft(void)
{
left_speed_duty=-MID_SPEED_DUTY;
right_speed_duty=MID_SPEED_DUTY;
}
//向右
void CarRight(void)
{
left_speed_duty=MID_SPEED_DUTY;
right_speed_duty=-MID_SPEED_DUTY;
}
//停止
void CarStop(void)
{
left_speed_duty=MIN_SPEED_DUTY;
right_speed_duty=MIN_SPEED_DUTY;
}
//向后
void CarBack(void)
{
left_speed_duty=-MAX_SPEED_DUTY;
right_speed_duty=-MAX_SPEED_DUTY;
}
void MotorInit(void)
{
MotorGPIO_Configuration();
CarStop();
}
//X挡前进
void X_CarGo(void)
{
left_speed_duty=X_SPEED_DUTY;
right_speed_duty=X_SPEED_DUTY;
}
//Y
void Y_CarGo(void)
{
left_speed_duty=Y_SPEED_DUTY;
right_speed_duty=Y_SPEED_DUTY;
}
//P
void P_CarGo(void)
{
left_speed_duty=P_SPEED_DUTY;
right_speed_duty=P_SPEED_DUTY;
}
//Q
void Q_CarGo(void)
{
left_speed_duty=Q_SPEED_DUTY;
right_speed_duty=Q_SPEED_DUTY;
}
//Z
void Z_CarGo(void)
{
left_speed_duty=Z_SPEED_DUTY;
right_speed_duty=Z_SPEED_DUTY;
}
motor.h
#ifndef __MOTOR_H_
#define __MOTOR_H_
#include "stm32f10x.h"
extern uint16_t speed_count;//占空比计数器 50次一周期
extern int8_t left_speed_duty;
extern int8_t right_speed_duty;
void CarMove(void);
void CarGo(void);
void CarBack(void);
void CarLeft(void);
void CarRight(void);
void CarStop(void);
void MotorInit(void);
void X_CarGo(void);
void Y_CarGo(void);
void Z_CarGo(void);
void P_CarGo(void);
void Q_CarGo(void);
#endif
main.c
#include "stm32f10x.h"
#include "motor.h"
#include "interface.h"
//全局变量定义
uint16_t speed_count=0;//占空比计数器 50周期一次
int8_t left_speed_duty=MIN_SPEED_DUTY;
int8_t right_speed_duty=MIN_SPEED_DUTY;
uint8_t tick_1s = 0;//1s计数器
uint8_t tick_100ms = 0;//100ms计数器
uint8_t tick_1ms = 0;//1ms计数器
uint8_t continue_time=0;
void GPIO_Conf(void);
void RCC_Conf(void);
void TIM2_Init(void);
void MotorInit(void);
int main(void)
{
uint16_t i,j,time;
RCC_Conf();
GPIO_Conf();
TIM2_Init();
MotorInit();
for(i=0;i<10000;i++)
for(j=0;j<1000;j++);//启动延迟模块
//寻迹模块
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)== 1&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)== 1)
left_speed_duty=68;
right_speed_duty=65;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)== 0&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)== 1)
{
left_speed_duty=68;
right_speed_duty=-65;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)== 1&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)== 0)
{
left_speed_duty=-68;
right_speed_duty=65;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6)== 0&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8)== 0)
{
left_speed_duty=0;
right_speed_duty=0;
}
}
}
void RCC_Conf(void)
{
ErrorStatus HSEStartUPStatus;
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUPStatus=RCC_WaitForHSEStartUp();
if(HSEStartUPStatus == SUCCESS)
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource()!=0x08);
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
}
//多线程的中断安装可以使电机运动的更稳定
void TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM2_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //RCC_APB1Periph_TIM2
TIM_TimeBaseStructInit(&TIM2_TimeBaseStructure);
TIM2_TimeBaseStructure.TIM_Prescaler = 72-1; //分频之后的时钟频率是1MHz
TIM2_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM2_TimeBaseStructure.TIM_Period = 100-1; //定时0.1ms
TIM2_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM2, &TIM2_TimeBaseStructure);
TIM_UpdateRequestConfig(TIM2, TIM_UpdateSource_Regular);
TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
//引脚的初始化函数
void GPIO_Conf(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=LED_PI;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
tick_1ms++;
if(tick_1ms > 10)
{ //1ms定时到
tick_1ms = 0;
speed_count++;
tick_100ms++;
if(speed_count > 100)
{
speed_count = 0;
}
if(tick_100ms > 50)
{
tick_100ms = 0;
tick_1s++;
}
CarMove();
}
}
}
interface.h
#ifndef __INTERFACE_H_
#define __INTERFACE_H_
#include "stm32f10x.h"
//user LED
#define LEFT_F_PIN GPIO_Pin_11
#define LEFT_F_GPIO GPIOB
#define LEFT_F_SET GPIO_SetBits(LEFT_F_GPIO , LEFT_F_PIN)
#define LEFT_F_RESET GPIO_ResetBits(LEFT_F_GPIO , LEFT_F_PIN)
#define LEFT_B_PIN GPIO_Pin_10
#define LEFT_B_GPIO GPIOB
#define LEFT_B_SET GPIO_SetBits(LEFT_B_GPIO , LEFT_B_PIN)
#define LEFT_B_RESET GPIO_ResetBits(LEFT_B_GPIO , LEFT_B_PIN)
#define RIGHT_F_PIN GPIO_Pin_12
#define RIGHT_F_GPIO GPIOB
#define RIGHT_F_SET GPIO_SetBits(RIGHT_F_GPIO , RIGHT_F_PIN)
#define RIGHT_F_RESET GPIO_ResetBits(RIGHT_F_GPIO , RIGHT_F_PIN)
#define RIGHT_B_PIN GPIO_Pin_13
#define RIGHT_B_GPIO GPIOB
#define RIGHT_B_SET GPIO_SetBits(RIGHT_B_GPIO , RIGHT_B_PIN)
#define RIGHT_B_RESET GPIO_ResetBits(RIGHT_B_GPIO , RIGHT_B_PIN)
#define LEFT_GO LEFT_F_SET; LEFT_B_RESET
#define LEFT_BACK LEFT_F_RESET; LEFT_B_SET
#define LEFT_STOP LEFT_F_RESET; LEFT_B_RESET
#define RIGHT_GO RIGHT_F_SET; RIGHT_B_RESET
#define RIGHT_BACK RIGHT_F_RESET;RIGHT_B_SET
#define RIGHT_STOP RIGHT_F_RESET;RIGHT_B_RESET
#define MAX_SPEED_DUTY 50 //默认占空比 按1ms最小分辨率 周期为50ms计算
#define MID_SPEED_DUTY 30
#define MIN_SPEED_DUTY 0
#define X_SPEED_DUTY 80
#define Y_SPEED_DUTY 70
#define P_SPEED_DUTY 60
#define Q_SPEED_DUTY 20
#define Z_SPEED_DUTY 40
#endif
基本信息
材料
STM32F103RCT6正点原子MINI板
三轮驱动小车
HL-1慧净电子小车底板
适配于
stm32F103系列 (需要修改程序)
连线图
小车连线:
·电源
小车J2 –VCC 接stm32控制板JP2- +5V
小车J2 –GND接stm32控制板JP2- GND
小车J2 –VCC接红外控制板VCC
小车J2 –GND接红外控制板GND
·驱动
小车J3-IN1接stm32控制板JP3-PB10
小车J3-IN2接stm32控制板JP3-PB11
小车J3-IN3接stm32控制板JP3-PB12
小车J3-IN4接stm32控制板JP3-PB13
小车J3-EN1接stm32控制板JP3-3.3V
小车J3-EN2接stm32控制板JP3-3.3V
·红外信号输出
小车J5-OUT1接stm32控制板JP2-PB6-----------R1
小车J5-OUT2接stm32控制板JP2-PB8-----------L1
红外控制板D01接stm32控制板J2-PB9--------R2
红外控制板D02接stm32控制板J2-PB7--------M
红外控制板D03接stm32控制板J2-PB5--------L2
关于左右方向,由面向小车的前进方向的左右确定
·红外信号输入
右端红外探头3线接红外控制板第一端3线(靠FC-35字端)
中间红外探头3线接红外控制板第二端3线
左端红外探头3线接红外控制板第三端3线
红外控制板第四端3线空闲
三线的接法:
探头端GND接控制板地
探头端OUT接控制板向里箭头↓
探头端IN 接控制板向外箭头↑
·电机
左电机接左面接线端,有箭头面朝上,
右电机接右面接线端,有箭头面朝上。
·关于小车指示灯:
L1 小车自带右寻迹探头指示灯
L2 小车自带左寻迹探头指示灯
当有红外信号返回时,指示灯亮,输出H电平;反之指示灯熄灭,输出低电平。
·与ST-LINK的连线
ST-LINK-SWD口CLK端接stm32控制板J1-TCK
ST-LINK-SWD口GND端接stm32控制板J1-GND
ST-LINK-SWD口IO端接stm32控制板J1-TMS
基本原理
小车底盘原理图
烧录
由于博主是学生,所以使用的是正点原子适配的串口烧录,烧录器件是FlyMcu。