基于STM32C8T6的智能小车

有幸赶上了今年的电赛,赛前用了一周多的时间准备迷宫小车赛题,苦于摄像头刚刚入门并不能实际应用,只能用红外传感器做出个半成品。

制作小车的硬件清单:

(1)小车框架

小车底层版*1、顶层版*1、面包板*2、、车轮*2、万向轮*1

(2)小车设备

STM32C8t6核心板*1、TB6612电机驱动*1、0.96OLED、JDY-31蓝牙模块、TCRT5000红外摄像头*6、电池、电机(带编码器)*2

 一、小车框架制作

底层:底层版淘宝上比较多 我用的是学校提供的两轮驱动板子  大致如下

 根据板子可以直接完成电机+轮子的固定和万向轮的安装

顶层:我们只在顶层粘贴了面包板并安装了摄像头待用,安装并不是很严格,只需和底层能对应上并保证稳定即可

设备安装:主要集中在顶层表面的面包板上,主要包括OLED、TB6612、核心板,顶层底面也粘贴面包板,出于电池线长度问题,因此将电源模块插在底层

二、整体思路

(1)驱动电机

我们采用TB6612作为电机驱动模块,由单片机产生pwm波和I/O口正负电压来控制电机的转速与方向。

//PWM.C
void PWM_Init(int16_t arr ,int16_t psc )
{
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO,ENABLE);//开启时钟

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;        // 初始化GPIO--PA8、PA11为复用推挽输出
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);  // 初始化定时器。
    TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;  //向上计数 0记到重装值
    TIM_TimeBaseInitStruct.TIM_Period=arr;
    TIM_TimeBaseInitStruct.TIM_Prescaler=psc;
    TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);   // TIM1
    
    TIM_OCInitTypeDef 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
    TIM_OC1Init(TIM1,&TIM_OCInitStruct);
    TIM_OC4Init(TIM1,&TIM_OCInitStruct);
    
    TIM_CtrlPWMOutputs(TIM1,ENABLE);// 高级定时器专属!!!--MOE主输出使能
    
    TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);// OC1预装载寄存器使能 //控制波形是立即生效还是定时器发生下一更新事件生效
    TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);// OC4预装载寄存器使能  //此处为下一次更新
    TIM_ARRPreloadConfig(TIM1,ENABLE);// TIM1在ARR上预装载寄存器使能
    
    TIM_Cmd(TIM1,ENABLE);           // 开定时器
    
    

}

void PWM_SetCompare(uint16_t Compare) //更改ccr值
{
    TIM_SetCompare1(TIM1, Compare);
    TIM_SetCompare4(TIM1, Compare);
    
}
//Motor.c
void Motor_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOB, GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);  
    
}

void Motor_SetSpeed(int16_t moto1,int16_t moto2)
{
    if (moto1 >= 0)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_12 );  
        GPIO_ResetBits(GPIOB, GPIO_Pin_13 );
    
    }
    else
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_12 );  
        GPIO_SetBits(GPIOB, GPIO_Pin_13);
        
    }
    if (moto2 >= 0)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_14); 
        GPIO_ResetBits(GPIOB, GPIO_Pin_15);
    }
    else
    {
    
        GPIO_ResetBits(GPIOB, GPIO_Pin_14);  
        GPIO_SetBits(GPIOB, GPIO_Pin_15);
        
    }
    TIM_SetCompare1(TIM1, 7200-myabs(moto1)); //7200-0 由小到大  计算结果为到7200的差距  上下相等即可
    TIM_SetCompare4(TIM1, myabs(moto2));  //0-7200 有小到大
}
//直行
void Motor_Straight(void)
{
    Motor_SetSpeed(3580,3600);
}
//右转
void Motor_RIGHT(void)
{
    Motor_SetSpeed(900,3600);
    
}
//大右转
void Motor_BIGRIGHT(void)
{
    Motor_SetSpeed(900,4800);
    
}
//左转
void Motor_LEFT(void)
{
    Motor_SetSpeed(3600,900);
    
}
//大左转
void Motor_BIGLEFT(void)
{
    Motor_SetSpeed(4800,900);
    
}
//刹车
void Motor_STOP(void)
{
    Motor_SetSpeed(0,0);
    Delay_ms(5);
}

//转圈
void Motor_Roll(void)
{
    Motor_SetSpeed(-3600,3600);
    
}
//限幅函数  
void Limit(int *motoA,int *motoB)
{
    if(*motoA>PWM_MAX)*motoA=PWM_MAX;
    if(*motoA<PWM_MIN)*motoA=PWM_MIN;
    
    if(*motoB>PWM_MAX)*motoB=PWM_MAX;
    if(*motoB<PWM_MIN)*motoB=PWM_MIN;
}
/**************************************************************************
函数功能:绝对值函数
入口参数:int
返回  值:unsigned int
**************************************************************************/
int myabs(int a)
{ 		   
    int temp;
    if(a<0)  
        temp=-a;  
    else temp=a;
    return temp;
}

最终实现效果:左右电机都正转(直行)、左右电机偏差(左转或右转)、左右电机反转(后退)

(2)红外传感器应用

我们采用六个红外传感器作为循迹判断,其中前面两个传感器用于判断终点,如果都识别到了黑线就停止,左前左后右前右后分别进行左转右转控制,只是转弯的速度角度不一样罢了。

//CountSensor.c
void CountSensor1_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
    
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line5 ;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
    EXTI_Init(&EXTI_InitStructure);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
}

此处只写出了初始化,我们对于黑线的识别没有采用中断函数的形式,而是直接写在了main函数中。

(3)蓝牙模块应用

蓝牙其实相当于另一种形式的串口,通过和手机相连,接受手机上发送的数据,然后传达给单片机,本质上和串口发送和接受的流程类似

//bluetooth
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	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_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)   //发送字符
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)  //发送数组
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)  //发送字符串
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)   //次方函数
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

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

int fputc(int ch, FILE *f)
{
	Serial_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);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
void BLUE_TOOTH_CONTROL(void)  //蓝牙控制函数
{
    if (Serial_GetRxFlag() == 1)
        {
            RxData = Serial_GetRxData();
            if(RxData == 0x41) //前进
            {
               Motor_Straight();
            }
            else if(RxData == 0x42)  //后退
            {
               Motor_Roll();
            }
            else if(RxData == 0x43) //左转
            {
                Motor_LEFT();
            }
            else if(RxData == 0x44)  //右转
            {
                Motor_RIGHT();
            }
            else if(RxData == 0x40)  //停车
            {
                Motor_STOP();
            }
        }
}

蓝牙使用界面 

 三、总结反思

1.车模选取

一个好的车模在前期搭建车模过程中可以省下不小功夫,不至于因为如何安装一个模块而大眼瞪小眼,建议选取一些配套的,孔比较多的模板来搭建(个人感觉这种比较方便)

2.模块提前测试好

各个外设在组合使用前我认为都应该进行单独的测试,我们在搭建过程中比较匆忙,没有系统的进行配合,导致后面倒车的时候发现只有一个轮子能反方向转,另一个不知道为什么转不了

3.提前对实物有一定的思路和概念

我认为对于设计的作品脑海中一定要有清晰的概念,其次就是有理论基础的支撑,我们的红外传感器就是因为开始设计不够精细,导致后面生掰的时候反而不好用了,必须要贴到地面才能识别出来4.一些外设

红外传感器尽量选择识别距离远的,然后就是一旦固定了就不要再动(真的哭死)

电池尽量选择能充电的

四、程序代码

链接:https://pan.baidu.com/s/1c8Jio4UbYpT1RzrwyBwBJQ 
提取码:tap2 
本次智能小车的制作为我们团队的第一次实践,感觉还有非常大的提升空间,欢迎各路大神多多指教

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
个人觉得STM32C8T6智能小车的仿真图可以分为以下几个部分:车身结构设计、电路连线图、传感器布局以及控制器配置。 首先,车身结构设计方面,可以包括主要组成部分如底盘、车轮和机械臂的构造以及它们之间的连接。通过仿真图,可以清晰地展示出小车的形状、尺寸以及每个组件的位置关系,帮助人们更好地理解和评估小车的设计方案。 其次,电路连线图方面,可以展示出电池、电机控制器、传感器和控制器之间的连接方式。通过仿真图,人们可以清楚地看到各个电路之间的连线情况,帮助了解整个系统的电路布局和连线方式。 接下来,传感器布局方面,可以展示出小车上各种传感器的位置和布局。例如,红外线传感器可以用于检测障碍物,超声波传感器可以用于测量距离等等。通过仿真图,可以清晰地呈现出这些传感器的安装位置和朝向,帮助人们了解小车的感知能力和应用场景。 最后,控制器配置方面,可以展示出用于控制小车运动和功能的主控板和相关配置信息。通过仿真图,可以清晰地展示出主控板的型号、输入输出接口以及与其他电路之间的连接方式,帮助人们根据仿真图来配置和调试小车的控制系统。 综上所述,通过STM32C8T6智能小车的仿真图,人们可以更好地了解和评估小车的设计方案,并进行必要的配置和调试工作。同时,仿真图也可以作为制作实物小车的参考,帮助人们进行物理构造和电路布线的工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值