串口控制小车(二次开发)

news2025/2/6 2:55:46

0.资料

项目工程文件夹

分文件原理

之前的代码

1.L9110S电机驱动模块demo

2.串口通信(习题4:PC发送字符串指令给单片机)

3.wifi模块(串口中断代码优化)

3.蓝牙模块


1.串口指令控制小车_分文件

1、和单片机的接线方式:与《L9110S电机驱动模块》一节中的连接方式一致

 

2、信号传递路线:

  1. 路线1:单片机P3^4、P3^5引脚 ——> L9110S电机驱动模块 ——> 小车左轮
  2. 路线2:单片机P3^2、P3^3引脚 ——> L9110S电机驱动模块 ——> 小车右轮
  3. 路线3:PC串口助手数据发送 ——> 单片机的RXD口 ——> 路线1/路线2 ——> 小车运动
  4. 路线4:单片机心跳包数据 ——> 单片机的TXD口 ——> PC串口助手显示

3、串口控制策略:

  1. 关键字眼确定:我们的有效指令在串口中断程序中方便随手处理,所以就不需要标志位。我用首字母'M'代表电机,关键字眼是:
    电机指令默认停止:“STOP”前进:“M1”后退:“M2”左前方转:“M3”右前方转:“M4”左后方转:“M5”右后方转:“M6”
  2. 判断方法:在小车不同方向的控制中,我们的有效指令的关键字眼的第一个字符都是'M',所以我们这里使用了switch语句来进行判断。格式如下:

    if(serial_buffer[0]=='M'){
    	switch(serial_buffer[1]){
            case '1':
                break;
            case '2':
                break;
            case '3':
                break;
            case '4':
                break;
            case '5':
                break;
            case '6':
                break;
            default :
                stop();
                break;
        }
    }

  3. 注意事项:在串口中断程序中需要注意memset()函数的书写位置,应该把memset()函数写在switch语句之中,而不是switch语句之后。否则会出现如下BUG:一个相同的指令需要发送两遍,单片机才会响应,只发送一遍指令是没有反应的。

    1. 具体为什么是这样,可以按照程序流程进行分析,上中,根据错误代码对serial_buffer字符数组的前两个元素进行了分析。
    2. 此外可以发现,第二次发送的指令只要第一个字符是'M',那么都可以驱动电机转动。
    3. 此外,其实在有效指令识别成功之后,其实是没有必要用memset()函数清零数组的,大部分情况下也不会出错。

 

4、主程序:【项目工程文件夹

  • 代码:
    #include "reg52.h"
    #include "motor.h"
    #include "delay.h"  
    #include "uart.h"
    
    void main(void)
    {
        UartInit();
    	while(1){
    		Delay1000ms();
    		sendString("hello\r\n");    //心跳包
    	}
    }

5、“串口”模块:

  • 思路:基于《L9110S电机驱动模块》的代码,本demo主要针对串口中断程序进行了修改。
  • 代码:
    #include "reg52.h"
    #include <string.h>
    #include "motor.h"
    
    #define len 12
    sfr AUXR = 0x8E;
    sbit ledD5 = P3^7;
    char serial_buffer[len];
    
    void Uart_Routine()    interrupt 4
    {
    	static int i = 0;	//静态全局区的变量,数组下标
    	char temp;
    	/* 中断处理程序中,对于接收中断的响应 */
    	if(RI == 1){	
    		RI = 0;//清除接收中断标志位
    		temp = SBUF;
    		if(temp=='M'){	//从数据缓冲寄存器SBUF中读到字符后,关心一件事
    			i = 0;
    		}
    		serial_buffer[i] = temp;
            i++;
            if(i == len){
                i = 0;
            } 
            //电机指令:默认--STOP;  M1--FORWARD;  M2--BACK;  M3--FORWARD_LEFT;  
            //         M4--FORWARD_RIGHT; M5--BACK_LEFT;  M6--BACK_RIGHT
    		if(serial_buffer[0]=='M'){
    			switch(serial_buffer[1]){
                    case '1':
                        goForward(); 
                        memset(serial_buffer, '\0', len);
                        break;
                    case '2':
                        goBack();
                        memset(serial_buffer, '\0', len);
                        break;
                    case '3':
                        goForward_left();
                        memset(serial_buffer, '\0', len);
                        break;
                    case '4':
                        goForward_right();
                        memset(serial_buffer, '\0', len);
                        break;
                    case '5':
                        goBack_left();
                        memset(serial_buffer, '\0', len);
                        break;
                    case '6':
                        goBack_right();
                        memset(serial_buffer, '\0', len);
                        break;
                    default :
                        stop();
                        break;
                }
    		}
    	}
    	/* 中断处理程序中,对于发送中断的响应 */
    	if(TI == 1){
    		// 暂时不做任何事情
    	}
    }
    
    void UartInit(void)		//9600bps@11.0592MHz
    {
    	AUXR = 0x01;
    	SCON = 0x50;	//8位UART,允许串口接收
    	TMOD &= 0xDF;
    	TMOD |= 0x20;	//定时器8位重载工作模式
    	TH1 = 0xFD;
    	TL1 = 0xFD;		//9600波特率初值
    	TR1 = 1;
    	EA = 1;
    	ES = 1;			//开启串口中断
    }
    
    void sendByte(char data_msg)
    {
    	SBUF = data_msg;
    	// Delay10ms();
    	while(TI == 0);
    	TI = 0;
    }
    
    void sendString(char *str)
    {
    	char *p = str;
    	while(*p != '\0'){
    		sendByte(*p);
    		p++;
    	}
    }

6、“电机”模块:

  • 代码:
    #include "reg52.h"
    
    sbit RightCon_1A = P3^2;   //L9110S的MotorB_1A--右轮电机下接触点
    sbit RightCon_1B = P3^3;   //L9110S的MotorB_1B--右轮电机上接触点
    sbit LeftCon_1A  = P3^4;   //L9110S的MotorA_1A--左轮电机下接触点
    sbit LeftCon_1B  = P3^5;   //L9110S的MotorA_1B--左轮电机上接触点
    
    void goForward()
    {
        /* 右轮:前进 */
        RightCon_1A = 0;
        RightCon_1B = 1;
        /* 左轮:前进 */
        LeftCon_1A = 0;
        LeftCon_1B = 1;
    }
    
    void goBack()
    {
        /* 右轮:倒退 */
        RightCon_1A = 1;
        RightCon_1B = 0;
        /* 左轮:倒退 */
        LeftCon_1A = 1;
        LeftCon_1B = 0;
    }
    
    void goForward_left()
    {
        /* 右轮:前进 */
        RightCon_1A = 0;
        RightCon_1B = 1;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }
    
    void goForward_right()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:前进 */
        LeftCon_1A = 0;
        LeftCon_1B = 1;
    }
    
    void goBack_left()
    {
        /* 右轮:倒退 */
        RightCon_1A = 1;
        RightCon_1B = 0;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }
    
    void goBack_right()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:倒退 */
        LeftCon_1A = 1;
        LeftCon_1B = 0;
    }
    
    void stop()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }

7、“延时”模块:

  • 代码:
    #include "intrins.h"
    
    void Delay1000ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    
    	_nop_();
    	i = 8;
    	j = 1;
    	k = 243;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }

2.蓝牙小车的点动控制_分文件

1、和单片机的接线方式:

 

  • 注:由于小车的电源由电池直接提供,所以在这种连接方式下,单片机开发板上的重启开关是否开启都不会影响单片机和小车模块各模块工作。

2、信号传递路线:

  1. 路线1:单片机P3^4、P3^5引脚 ——> L9110S电机驱动模块 ——> 小车左轮
  2. 路线2:单片机P3^2、P3^3引脚 ——> L9110S电机驱动模块 ——> 小车右轮
  3. 路线3:蓝牙APP透传数据发送 ——> HC-08蓝牙模块的RXD引脚 ——> 单片机的TXD口 ——> 路线1/路线2 ——> 小车运动

3、蓝牙APP自定义按键:我们用现成的APP,按键设置如下图所示

 

4、点动控制策略:

  1. 单片机程序方面:在串口指令控制小车的基础上,接入蓝牙模块,通过蓝牙控制小车。
    1. 添加点动控制,我们需要通过修改“串口”模块的代码来实现该功能。在小车没有收到指令时,就让小车停留在一个停止的状态。那么其中必定涉及到收到有效指令后电机转多久的问题,否则不可能让小车动起来,所以需要找到一个合适的延时点。这里我设置的是在串口中断程序中,小车运动延时10ms,这不会太短也不会太长。
    2. 原先的心跳包发送功能在这个demo中不能被使用了。因为主进程while(1)之中不能用来每隔一秒发送心跳包了,而是被停转电机的函数占用。这样我们才能保证时刻让小车保持在停止状态。
  2. 蓝牙APP方面
    1. 如果APP支持长按时一直发数据,松开就停止发数据,就能实现前进按键长按下后小车才一直往前走的功能,但是现有APP的自定义按键不能实现,需要我们自己来制作安卓app(我们可以称之为一种上位机)。
    2. 虽然说我们现在还不会制作app,但是我们可以通过PC上串口助手的“自动发送数据”功能初步体验这种点动效果。
  3. 注意事项
    1. 因为现在由电池盒来给单片机供电,但是下载程序的时候电脑也给单片机供电,所以我们下载程序的时候最好将这个电源线拔掉,避免造成单片机损坏。 烧录完成后,如果单片机插着由电池盒提供的电源,并且此时还和PC用USB线连接着,那么最好不要开启开发板上的重启开关。
    2. 烧录代码时,可能会出现以下警告:“UNCALLED SEGMENT”,这是因为我们在“uart.c”源文件中通过包含头文件“”对Delay1000ms();函数进行了声明,但是“uart.c”中并没有调用它。这个警告可以忽略

5、主程序“main”:【项目工程文件夹

  • 代码:相较于本节的串口指令控制小车demo修改了主进程while(1)
    #include "reg52.h"
    #include "motor.h"
    #include "delay.h"  
    #include "uart.h"
    
    void main(void)
    {
        UartInit();
    	while(1){
    		stop();
    	}
    }

6、“串口”模块:

  • 代码:相较于本节的串口指令控制小车demo修改了串口中断程序。
    #include "reg52.h"
    #include <string.h>
    #include "motor.h"
    #include "delay.h"
    
    #define len 12
    sfr AUXR = 0x8E;
    sbit ledD5 = P3^7;
    char serial_buffer[len];
    
    void Uart_Routine()    interrupt 4
    {
    	static int i = 0;	//静态全局区的变量,数组下标
    	char temp;
    	/* 中断处理程序中,对于接收中断的响应 */
    	if(RI == 1){	
    		RI = 0;//清除接收中断标志位
    		temp = SBUF;
    		if(temp=='M'){	//从数据缓冲寄存器SBUF中读到字符后,关心一件事
    			i = 0;
    		}
    		serial_buffer[i] = temp;
            i++;
            if(i == len){
                i = 0;
            } 
            //电机指令:默认--STOP;  M1--FORWARD;  M2--BACK;  M3--FORWARD_LEFT;  
            //         M4--FORWARD_RIGHT; M5--BACK_LEFT;  M6--BACK_RIGHT
    		if(serial_buffer[0]=='M'){
    			switch(serial_buffer[1]){
                    case '1':
                        goForward(); 
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    case '2':
                        goBack();
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    case '3':
                        goForward_left();
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    case '4':
                        goForward_right();
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    case '5':
                        goBack_left();
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    case '6':
                        goBack_right();
                        memset(serial_buffer, '\0', len);
    					Delay10ms();
                        break;
                    default :
                        stop();
                        break;
                }
    		}
    	}
    	/* 中断处理程序中,对于发送中断的响应 */
    	if(TI == 1){
    		// 暂时不做任何事情
    	}
    }
    
    void UartInit(void)		//9600bps@11.0592MHz
    {
    	AUXR = 0x01;
    	SCON = 0x50;	//8位UART,允许串口接收
    	TMOD &= 0xDF;
    	TMOD |= 0x20;	//定时器8位重载工作模式
    	TH1 = 0xFD;
    	TL1 = 0xFD;		//9600波特率初值
    	TR1 = 1;
    	EA = 1;
    	ES = 1;			//开启串口中断
    }
    
    void sendByte(char data_msg)
    {
    	SBUF = data_msg;
    	// Delay10ms();
    	while(TI == 0);
    	TI = 0;
    }
    
    void sendString(char *str)
    {
    	char *p = str;
    	while(*p != '\0'){
    		sendByte(*p);
    		p++;
    	}
    }
    

7、“电机”模块:

  • 代码:相较于本节的串口指令控制小车demo没有任何改变
    #include "reg52.h"
    
    sbit RightCon_1A = P3^2;   //L9110S的MotorB_1A--右轮电机下接触点
    sbit RightCon_1B = P3^3;   //L9110S的MotorB_1B--右轮电机上接触点
    sbit LeftCon_1A  = P3^4;   //L9110S的MotorA_1A--左轮电机下接触点
    sbit LeftCon_1B  = P3^5;   //L9110S的MotorA_1B--左轮电机上接触点
    
    void goForward()
    {
        /* 右轮:前进 */
        RightCon_1A = 0;
        RightCon_1B = 1;
        /* 左轮:前进 */
        LeftCon_1A = 0;
        LeftCon_1B = 1;
    }
    
    void goBack()
    {
        /* 右轮:倒退 */
        RightCon_1A = 1;
        RightCon_1B = 0;
        /* 左轮:倒退 */
        LeftCon_1A = 1;
        LeftCon_1B = 0;
    }
    
    void goForward_left()
    {
        /* 右轮:前进 */
        RightCon_1A = 0;
        RightCon_1B = 1;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }
    
    void goForward_right()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:前进 */
        LeftCon_1A = 0;
        LeftCon_1B = 1;
    }
    
    void goBack_left()
    {
        /* 右轮:倒退 */
        RightCon_1A = 1;
        RightCon_1B = 0;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }
    
    void goBack_right()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:倒退 */
        LeftCon_1A = 1;
        LeftCon_1B = 0;
    }
    
    void stop()
    {
        /* 右轮:不转 */
        RightCon_1A = 0;
        RightCon_1B = 0;
        /* 左轮:不转 */
        LeftCon_1A = 0;
        LeftCon_1B = 0;
    }

8、“延时”模块:

  • 代码:相较于本节的串口指令控制小车demo增加了延时10ms的函数
    #include "intrins.h"
    
    void Delay1000ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    
    	_nop_();
    	i = 8;
    	j = 1;
    	k = 243;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void Delay10ms()		//@11.0592MHz
    {
    	unsigned char i, j;
    
    	i = 18;
    	j = 235;
    	do
    	{
    		while (--j);
    	} while (--i);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MVC、MVP、MVVM:详解2

概述 MVC、MVP、MVVM 都是在 Android 开发中经常用到的架构思想&#xff0c;它们都是为了更好地分离代码、提高代码可复用性、方便维护等目的而设计的。下面对这三种架构思想进行简单的介绍和比较。 MVC MVC 架构是最早被使用的一种架构&#xff0c;它把程序分成了三个部分&…

CVPR 2023 首届视觉异常检测(Visual Anomaly and Novelty Detection,VAND)挑战赛 Zero-shot 赛道冠军

这篇文章主要介绍一下我们在 CVPR 2023 VAND Workshop 的挑战赛中所采用的模型和方案。在 Zero-shot 赛道中我们获得了冠军&#xff08;Winner&#xff09;&#xff0c;在 Few-shot 赛道中&#xff0c;我们获得了第四名&#xff08;Honorable Mentions&#xff09;。 题目&…

怎么安装anaconda?anaconda安装详解!

Anaconda Navigator 是 Anaconda 的图形化管理界面&#xff0c;点击它即可进入 Anaconda 的图像化管理界面。许多小伙伴可能被朋友或者小编安利过Anaconda而跃跃欲试&#xff0c;今天小编就将Anaconda安装详解分享给大家。心动的小伙伴们赶紧安装起来吧&#xff01; 下载 官方…

1分钟教你配置好你的python环境

欢迎来到我们的系列博客《Python360全景》&#xff01;在这个系列中&#xff0c;我们将带领你从Python的基础知识开始&#xff0c;一步步深入到高级话题&#xff0c;帮助你掌握这门强大而灵活的编程语法。无论你是编程新手&#xff0c;还是有一定基础的开发者&#xff0c;这个系…

不能发现BUG的测试用例不是好的测试用例吗?

一般情况下技术岗面试都需要经历面试和笔试部分&#xff0c;面试过程中主要采用问答的形式&#xff0c;一般没有完全固定的回答&#xff0c;主要是根据自己的工作经验应答面试官的问题&#xff0c;而笔试部分更注重基础知识以及问题的常规解决方案。下面IT技术宅男为大家整理了…

C++案例

目录 一、while循环猜数组 二、 水仙花数 三、for循环敲桌子游戏 四、99乘法表 五、一维数组--元素逆置 六、冒泡排序 七、封装一个函数--利用冒泡排序&#xff0c;实现对整型数组的升序排序 八、结构体嵌套结构体 九、结构体排序 一、while循环猜数组 说明&#x…

经验总结:13 条自动化测试框架设计原则

1.代码规范 测试框架随着业务推进&#xff0c;必然会涉及代码的二次开发&#xff0c;所以代码编写应符合通用规范&#xff0c;代码命名符合业界标准&#xff0c;并且代码层次清晰。特别在大型项目、多人协作型项目中&#xff0c;如果代码没有良好的规范&#xff0c;那么整个框…

使用@Schedule注解实现定时任务,多线程执行定时任务,Cron表达式详解

Schedule注解实现定时任务&#xff0c;多线程执行定时任务&#xff0c;Cron表达式详解 使用Schedule注解实现定时任务Scheduled注解多线程执行定时任务Cron表达式Cron中的通配符 使用Schedule注解实现定时任务 1、首先&#xff0c;在项目启动类上添加 EnableScheduling 注解&am…

Vue CLI 全局事件总线 消息的订阅与发布

3.10. 全局事件总线&#xff08;GlobalEventBus&#xff09; 一种可以在任意组件间通信的方式&#xff0c;本质上就是一个对象&#xff0c;它必须满足以下条件 所有的组件对象都必须能看见他这个对象必须能够使用$on $emit $off方法去绑定、触发和解绑事件 使用步骤 定义全…

MySQL数据库基础 12

第十二章 MySQL数据类型 1. MySQL中的数据类型2. 整数类型2.1 类型介绍2.2 可选属性2.2.1 M2.2.2 UNSIGNED2.2.3 ZEROFILL 2.3 适用场景2.4 如何选择&#xff1f; 3. 浮点类型3.1 类型介绍3.2 数据精度说明3.3 精度误差说明 4. 定点数类型4.1 类型介绍 5. 位类型&#xff1a;BI…

CSS--Java EE

在前端的代码中&#xff0c;CSS 相关的代码写在什么位置呢&#xff1f; CSS 可以写在<style>标签中外部引入&#xff1a;输入 link: css写在 div 标签中 目录 一、选择器的种类 1 基础选择器 1.1 类选择器 1.2 id选择器 1.3 标签选择器 1.4 通用选择器 小结 2 …

Spring Security6 全新写法,大变样!

文章目录 1. WebSecurityConfigurerAdapter2. 使用 Lambda3. 自定义 JSON 登录3.1 自定义 JSON 登录3.1.1 自定义登录过滤器3.1.2 自定义登录接口 3.2 原因分析3.3 问题解决 Spring Security 在最近几个版本中配置的写法都有一些变化&#xff0c;很多常见的方法都废弃了&#x…

Java-IO流基础知识

目录 1.File类与路径知识 1.File类 2.Java中的路径知识 3.创建File类的实例 4.File类的方法使用 5.File类使用的注意点 2.IO流知识 1.IO流原理 2.文件的读入 3.read()的重载方法&#xff1a;难点 4.文件的写出 1.写出的说明 2.写出操作的具体步骤 5.文件的复制&am…

测试工程师如何有效的编写bug报告?

为什么要求有效的缺陷报告&#xff1f; 缺陷报告是测试过程中最重要的部分&#xff0c;对产品的质量有较大的影响&#xff0c;是测试人员价值的终极体现。好的缺陷报告可以减少研发部门的二次缺陷率、提高研发修改缺陷的速度、提高测试部门的信用度、增强测试和研发部门的协作…

Resful API是什么

文章目录 摘要1、RESTful API是什么&#xff1f;2、RESTful是什么&#xff1f;参考资料 摘要 RESTful是整个网络应用程序设计风格和开发方式。而RESTful API是其中API的设计风格。 1、RESTful API是什么&#xff1f; API接口在设计命名时&#xff0c;由版本/操作资源名称/操…

【Spring Boot 初识丨六】依赖注入

上一篇讲了 Spring Boot 的beans 本篇来讲一讲 依赖注入 Spring Boot 初识&#xff1a; 【Spring Boot 初识丨一】入门实战 【Spring Boot 初识丨二】maven 【Spring Boot 初识丨三】starter 【Spring Boot 初识丨四】主应用类 【Spring Boot 初识丨五】beans 依赖注入 一、 定…

攻防世界-Crypto-easychallenge

题目描述&#xff1a;将文件下载下来&#xff0c;只有一个pyc文件 1. 思路分析 先向chatgpt问下什么是pyc文件&#xff1a; OK&#xff0c;这里简单总结下&#xff1a; 1. pyc文件是python源码编译后的生成的二进制文件 2. 通过一些库可以逆向出pyc的源代码 那么我们需要做…

数组题目总结 -- 单调栈问题

目录 零. 单调栈一. Next Greater Element(单调栈问题模板)题目简述&#xff1a;思路和代码&#xff1a;I. 思路II. 代码 二. Next Warmer Weather题目简述&#xff1a;思路和代码&#xff1a;I. 思路II. 代码 三. Next Greater Elements&#xff08;循环数组&#xff09;题目简…

UG\NX二次开发 装配下的点坐标

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介: UF_CURVE_create_point 创建一个点 UF_CSYS_create_temp_csys 创建临时坐标系 以上两个函数都有一个点坐标输入,而且都是输入的绝对坐标系下的点坐标值。下面验证在装配下两个绝对坐标值具体指 “当…

【三维视觉】空间点集的最小包围盒计算

0 问题描述 假设有一个空间点集&#xff0c;不重合的点数有N个。 N1时&#xff0c;最小包围盒是一个点&#xff1a;中心为其本身&#xff0c;半径无穷小 N2时&#xff0c;最小包围盒是一个圆&#xff1a;中心为连线中点&#xff0c;半径为边长一半 N3时&#xff0c;不共线的三…