智能小车论文单片机循迹测速避障液晶显示 - 图文

更新时间:2024-04-18 19:28:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

“好帮手”电子设计竞赛

题目:智能小车参赛者:

指导老师:

1

摘要

近年来,随着我国经济建设的高速发展,机动车辆拥有量也在急剧增长,交通事故也日益增多,车辆超速成为了越来越严重的问题。而我国生产的汽车、摩托车电机转速测量系统大多使用动圈式模拟测速。这种测量系统存在精度差、过载能力弱等缺点。

本次的智能仪表综合训练的主要任务是设计一个智能小车,要求实现小车能够直走、通过光电传感器进行测速、通过PWM电路模块进行调速以及通过LCD12864液晶模块进行小车速度、路程、距离的显示。控制板的设计以8位的STC89C52单片机为控制核心,驱动板则以L289N驱动芯片为核心,应用光电传感器和LCD液晶模块,成功的实现了小车的测速、显示功能、超声波测距、无线电控制等功能。

关键词:智能小车;光电传感器;驱动芯片;LCD液晶模块;无线电控制

2

目录

第一章 绪论 ................................................................................................................................. 4

1.1 问题的提出 ...................................................................................................................... 4 第二章 智能小车原理 ................................................................................................................... 5

2.1设计思路 ........................................................................................................................... 5 2.2 STC89C52RC单片机简介 .................................................................................................. 6 2.3 小车驱动板简介 .............................................................................................................. 6 2.4 小车驱动方式选择 .......................................................................................................... 7 2.5 光电测速原理 .................................................................................................................. 8 2.6 LCD12864显示模块 ......................................................................................................... 8 2.7 无线电原理 ...................................................................................................................... 9 第三章 系统硬件设计 ................................................................................................................. 10

3.1车体结构及其驱动电路 ................................................................................................. 10 3.2循迹功能 ......................................................................................................................... 10 3.3 测速模块的设计 ............................................................................................................ 11 3.3 PWM调速模块的设计 ................................................................................................... 12 3.4 无线电模块 .................................................................................................................... 12 3.5 超声波测距 .................................................................................................................... 13 3.6 LCD12864显示 ............................................................................................................... 14 第四章 系统软件的设计 ............................................................................................................. 14

4.1 循迹与无线电接收功能单片机程序 ............................................................................ 14 4.2 LCD12864液晶显示程序................................................................................................ 26 总结 ............................................................................................................................................... 41 附录1 PCB原理图 ....................................................................................................................... 42

附录1-1 电机原理图........................................................................................................... 42 附录1-5 LCD12864显示PCB图 .......................................................................................... 45 附录2 实物图 ...................................................................................................................... 46

3

第一章 绪论

1.1 问题的提出

当今世界,科学技术日新月异。传感器技术和自动控制正在飞速发展,机械、电气和电子信息已经不再明显分家,自动控制在工业领域中的地位已经越来越重要,“智能”这个词也已经成为了热门词汇。

作为机械行业的代表产品——汽车,其与电子信息产业的融合速度也显著提高,呈现出两个明显的特点:一是电子装置占汽车整体(特别是轿车)的价值量比例逐步提高,汽车将由以机械产品为主向高级的机电一体化方向发展,汽车电子产业也很可能成为依托整车制造业和用车提升配置而快速成为新的增长点:二是汽车开始向电子化、多媒体化和智能化方向发展,使其不仅作为一种代步工具、同时具有交通、娱乐、办公和通讯等多种功能。

勿容置疑,机电一体化人才的培养不论是在国外还是在国内,都开始重视起来,主要表现在大学生的各种大型的创新比赛,比如:亚洲广播电视联盟亚太地区机器人大赛、全国大学生“飞思卡尔”杯智能汽车竞赛等众多重要竞赛都能很好的培养大学生对机电一体化的兴趣与强化机电一体化的相关知识。所以作为机电一体化学生,必须加倍努力,为逐步赶上国外先进水平并超过之而努力。

这项设计是以采购的小车为基础,采用8位STC89C52RC单片机作为控制核心,逐步实现测速、调速、显示这三大功能。

4

第二章 智能小车原理

2.1设计思路

本次小车设计的硬件框图如2-1图所示;利用两块STC89C52单片机在小车上进行智能控制。

PWM调速 电源 电源 测速 无线电控制 单片机 STC89C52 (1) 测速 单片机 STC89C52 (2) 超声波测距 电机驱动板 LCD12864显示

我们的智能小车是基于51单片机的智能小车系统。功能如下: 1、通过光电传感器小车循迹。

2、测速传感器为光电测速传感器,在单位时间内计算脉冲的次数,然后再进行转换和处理即得到所测量的速度。 3、通过PWM调速模块结合软件进行调速。 4、通过LCD12864液晶屏显示速度、路程、距离。

5

5、通过超声波测距测量小车前面障碍物距离。 6、通过无线电控制小车行驶方向。

2.2 STC89C52RC单片机简介

单片机STC89C52如下2-2图所示

2-2图 单片机

STC89C52是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C52为众多嵌入式控制应用系统提供高灵活、非常有效的解决方案。具有以下标准功能:8k字节Flash,512字节RAM, 32 位I/O 口线,看门狗定时器,内置4KB EEPROM,MAX810复位电路,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口。另外 STC89X52 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。最高运作频率35MHz,6T/12T可选。

2.3 小车驱动板简介

小车驱动板如2-4图所示

6

2-4小车驱动板

L289N模块输入输出关系如下表所示 ENA H H H L IN1 H L IN1 X IN2 L H IN2 X 图

电机运转情况 正转 反转 快速停止 停止 2-1表

2.4 小车驱动方式选择

采用由集成了双极性管组成的H桥电路芯片L298N。用单片机控制晶体管使之工作在

7

占空比可调的开关状态,精确调整电机转速。这种电路由于工作在管子的饱和截止模式下,效率非常高;H桥电路保证了可以简单地实现转速和方向的控制;电子开关的速度很快,稳定性也很高,是一种广泛采用的调速技术。

2.5 光电测速原理

小车速度检测方案的设计是个不可忽视的问题。只有选择好测速方案,才能省时、省力、省线地去做设计。

原理是传感器开孔圆盘的转轴与减速电机转轴相连,光源的光通过开孔盘的孔和缝隙反射到光敏元件上,开孔盘旋转体转一周,光敏元件上照到光的次数等于盘上的开孔数,从而测出旋转体旋转速度。灵敏度较高,但容易受外界光源的影响。虽然光电传感器受外界光源影响很大,但是它使用方便、安装简单,还有本设计要求的准确度不是很高,因此就选择了光电测速传感器。

光电测速传感器的原理图如下图所示

光电测速模块如2-5图所示

2.6 LCD12864显示模块

系统采用12864液晶显示,它不仅节省了单片机的资源,相比较数码管液晶显示更加直观、节能,同时可以直接显示中文汉字、字符等,具有灵活易操作的特性。故采用LCD显示。

8

2.7 无线电原理

XL24L01P-D01是采用挪威NORDIC公司的nrf24L01p 2.4G无线收发IC设计的一款高性能2.4G无线收发模块,采用GFSK调制,工作在2400-2483M的国际通用ISM频段,最高调制速率可达2MBPS。XL24L01P-D01集成了所有与RF协议相关的高速信号处理部分,如:自动重发丢失数据包和自动产生应答信号等,模块的SPI接口可以利用单片机的硬件SPI口连接或用单片机的I/O口进行模拟,内部有FIFO可以与各种高低速微处理器接口,便于使用低成本单片机

9

第三章 系统硬件设计

3.1车体结构及其驱动电路

硬件部分则在采购的小车基础上进行,小车的实物图如图3-1所示。

3-1图

3.2循迹功能

智能小车利用的光电传感器进行循迹功能。如图3-2所示。

10

光电循迹

3.3 测速模块的设计

光电传感器是通过把光强度的变化转换成电信号的变化来实现控制的。光电传感器在一般情况下,有三部分构成,它们分为:发送器、接收器和检测电路。其安装在小车上如图所示。

3-3图 安装图

光电测 速 11

3.3 PWM调速模块的设计

调速模块决定了小车的调速功能,本次设计使用PWM进行调速。 脉冲宽度调制(PWM)是英文“Pulse Width Modulation”的缩写,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量,通信,功率控制与变换等许多领域。一种模拟控制方式,根据相应载荷的变化来调制晶体管栅极或基极的偏置,来实现开关稳压电源输出晶体管或晶体管导通时间的改变,这种方式能使电源的输出电压在工作条件变化时保持恒定。

脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON) 或断(OFF)的重复脉序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。

3.4 无线电模块

利用无线电XL24L01P-D01进行控制小车在非循迹状态的前走、后退、左转、右转、暂停的功能。并控制小车进入循迹状态。

12

3.5 超声波测距

超声波测距采用IO口TRIG触发测距,给至少10us高电平信号;模块自动发送8个40KHZ的方波,自动检测是否有信号返回;有信号返回,通过IO口ECHO输出以后高电平,高电平持续时间就是方波从发射到返回的时间。如图所示:

13

3.6 LCD12864显示

用LCD12864显示“小车路程、小车左轮速度、小车右伦速度、小车前方障碍物距离”的具体信息。如图所示:

第四章 系统软件的设计

4.1 循迹与无线电接收功能单片机程序

#include #include //#include<12864cusu.c> #include typedef unsigned char uchar; typedef unsigned char uint;

//****************************************IO***************************************

14

sbit CE =P1^5; 同决定NRF24L01的状态

sbit CSN =P1^0; sbit SCK =P1^4; sbit MOSI=P1^1; sbit MISO=P1^3; sbit IRQ =P1^2;

//SPI片选使能,低电平使能 //SPI时钟 //SPI串行输入 //SPI串行输出 //中断,低电平使能*/

0-9

//工作模式控制,在CSN为低的情况下,CE协同NRF24L01的CONFIG寄存器共

//******************************************************************************

uchar seg[10]={0xC0,0xCF,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //0~~9段码 //

//**********************************************************************************

sbit KEY1=P3^2; sbit KEY2=P3^3; sbit KEY3=P3^4; sbit KEY4=P3^5;

//***********************************

**************************************************

sbit led1=P0^7; sbit led0=P0^6;

//*********************************************NRF24L01*************************************

#define TX_ADR_WIDTH 5 #define RX_ADR_WIDTH 5 #define TX_PLOAD_WIDTH 32 #define RX_PLOAD_WIDTH 32

// 5字节发射地址宽度 // 5字节接收地址宽度 // 20 uints TX payload // 20 uints TX payload

uint const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址 uint const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址 //***************************************NRF24L01

15

*******************************************************

#define READ_REG 0x00 // 读寄存器指令,读操作的寄存器的地址,设置地址 AAAAA = 00000,以后要变更地址只需在该数值上加即可

#define WRITE_REG 0x20

// 写寄存器指令,写操作的寄存器的地址,设置地址 AAAAA

= 00000,以后要变更地址只需在该数值上加即可

#define RD_RX_PLOAD 0x61 // 读取接收数据指令(即读RX有效数据) #define WR_TX_PLOAD 0xA0 // 写待发数据指令(即写TX有效数据) #define FLUSH_TX 0xE1 // 冲洗发送 FIFO指令,(清除 TX FIFO 寄存器) #define FLUSH_RX 0xE2

// 冲洗接收 FIFO指令,(清除 RX FIFO 寄存器)

#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令 #define NOP 0xFF

// 保留 (空操作)

//*************************************SPI(nRF24L01)寄

****************************************************

#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式 #define EN_AA 0x01 // 自动应答功能设置 #define EN_RXADDR 0x02 // 可用信道设置 #define SETUP_AW 0x03 // 收发地址宽度设置 #define SETUP_RETR 0x04 // 自动重发功能设置 #define RF_CH 0x05 // 工作频率设置

#define RF_SETUP 0x06 // 发射速率、功耗功能设置 #define STATUS 0x07 // 状态寄存器 #define OBSERVE_TX 0x08 // 发送监测功能 #define CD 0x09 // 地址检测 #define RX_ADDR_P0 0x0A // 频道0接收数据地址 #define RX_ADDR_P1 0x0B // 频道1接收数据地址 #define RX_ADDR_P2 0x0C // 频道2接收数据地址 #define RX_ADDR_P3 0x0D // 频道3接收数据地址 #define RX_ADDR_P4 0x0E // 频道4接收数据地址 #define RX_ADDR_P5 0x0F // 频道5接收数据地址 #define TX_ADDR 0x10 // 发送地址寄存器

16

#define RX_PW_P0 0x11 // 接收频道0接收数据长度 #define RX_PW_P1 0x12 // 接收频道0接收数据长度 #define RX_PW_P2 0x13 // 接收频道0接收数据长度 #define RX_PW_P3 0x14 // 接收频道0接收数据长度 #define RX_PW_P4 0x15 // 接收频道0接收数据长度 #define RX_PW_P5 0x16 // 接收频道0接收数据长度 #define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置

//**************************************************************************************

void Delay(unsigned int s); void inerDelay_us(unsigned char n); void init_NRF24L01(void); uint SPI_RW(uint uchar); uchar SPI_Read(uchar reg); void SetRX_Mode(void);

uint SPI_RW_Reg(uchar reg, uchar value);

uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars); uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars); unsigned char nRF24L01_RxPacket(unsigned char* rx_buf); void nRF24L01_TxPacket(unsigned char * tx_buf); //**********************************************************************************

void Delay(unsigned int s) {

unsigned int i; for(i=0; i

//******************************************************************************************

17

uint bdata sta; //状态标志 sbit RX_DR sbit TX_DS

=sta^6; =sta^5;

sbit MAX_RT =sta^4;

/******************************************************************************************

/*延时函数

/******************************************************************************************/

void inerDelay_us(unsigned char n) {

for(;n>0;n--) }

//****************************************************************************************

/*NRF24L01初始化

//***************************************************************************************/

void init_NRF24L01(void) {

inerDelay_us(100);

CE=0; // CE 为低,处于待机模式 I CSN=1; // 不使能SPI片选信号 SCK=0; // Spi clock line init high _nop_();

SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // 写本地地址 SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); // 写接收端地址 SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 频道0自动

ACK应答允许

SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 允许接收地址只有频道0,如果需要多频道可以参考Page21

18

SPI_RW_Reg(WRITE_REG + RF_CH, 0); // 设置信道工作为2.4GHZ,收发必须一致 SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节

SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); 大值0dB

SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); 送

}

/****************************************************************************************************

/*函数:uint SPI_RW(uint uchar) /*功能:NRF24L01的SPI写时序

/****************************************************************************************************/

uint SPI_RW(uint uchar) {

uint bit_ctr;

for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit {

MOSI = (uchar & 0x80); // 将数据 uchar 的最高位赋值给 SPI数据输出脚 uchar = (uchar << 1); // 数据 uchar 左移一位 SCK = 1;

/* MOSI上的数据在时钟上升沿发送,注意:时钟上升沿

// IRQ收发完成中断响应,16位CRC,主发

//设置发射速率为1MHZ,发射功率为最

到来时,将数据 uchar的最高位移入MOSI,

同时,从设备的寄存器也将其当前存放的数据

的最高位移入MISO,

紧接着在SCK的下降沿的时候从设备的

MOSI 接收到了主机发送过来的数据,同时主机也接收到了从设备发送过来的数据.

*/

uchar |= MISO; // 将 MISO中的数据保存到数据 uchar 的最低位

19

SCK = 0;

// 时钟下降沿的时候将 MISO 上的电平存放到数据

uchar 的最低位

}

return(uchar); }

/****************************************************************************************************

/*函数:uchar SPI_Read(uchar reg) /*功能:NRF24L01的SPI读时序

/****************************************************************************************************/

uchar SPI_Read(uchar reg) {

uchar reg_val;

CSN = 0; // SPI片选信号,低电平使能,开启SPI数据传输

// 将从机寄存器中的数据返回

SPI_RW(reg); // 通过地址 reg 选择所要读的寄存器

reg_val = SPI_RW(0); // 读取所选寄存器当前存储的数据,将读到的值赋值给变量 reg_val 。 CSN = 1; // 禁止SPI数据传输 return(reg_val); // 将读到的寄存器的数据返回 }

/****************************************************************************************************/

/*功能:NRF24L01读写寄存器函数

/****************************************************************************************************/

uint SPI_RW_Reg(uchar reg, uchar value) {

uint status;

CSN = 0; // SPI片选信号,低电平使能,开启SPI数据传输

20

status = SPI_RW(reg); // 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的状态,赋值给变量 status

SPI_RW(value); // 向所选的寄存器中写入数据 CSN = 1; // 禁止SPI数据传输

return(status); // 将从寄存器读到的状态数据返回 }

/****************************************************************************************************/

/*函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)

/*功能: 用于读数据,reg:为寄存器地址,pBuf:为待读出数据地址,uchars:读出数据的个数 /****************************************************************************************************/

uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) {

uint status,uchar_ctr;

CSN = 0; status = SPI_RW(reg); 状态,赋值给变量 status

for(uchar_ctr=0;uchar_ctr

pBuf[uchar_ctr] = SPI_RW(0); // 将从寄存器读出来的数据存储在数组 pbuf[] 中。

// 禁止SPI数据传输

// SPI片选信号,低电平使能,开启SPI数据传输

// 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的

CSN = 1;

return(status); // 将从寄存器读到的状态数据返回 }

/*********************************************************************************************************

/*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)

/*功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数

21

/*********************************************************************************************************/

uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) {

uint status,uchar_ctr;

CSN = 0; //SPI使能

status = SPI_RW(reg); // 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的状态,赋值给变量 status

for(uchar_ctr=0; uchar_ctr

SPI_RW(*pBuf++);

// 将数据写入相应的寄存器中。

CSN = 1; //禁止SPI数据传输

return(status); //将从寄存器读到的状态数据返回 }

/****************************************************************************************************/

/*函数:void SetRX_Mode(void) /*功能:数据接收配置

/****************************************************************************************************/

void SetRX_Mode(void) { CE=0;

// CE 为低,处于待机模式 I

SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); CE = 1;

// IRQ收发完成中断响应,16位CRC ,主接收 // 接收模式由设置 CE 为高来启动 // 130 us 后 nRF24L01 开始检测空中信

inerDelay_us(130); 息。

}

/******************************************************************************************************/

/*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf)

22

/*功能:数据读取后放入rx_buf接收缓冲区中

/******************************************************************************************************/

unsigned char nRF24L01_RxPacket(unsigned char* rx_buf) {

unsigned char revale=0; sta=SPI_Read(STATUS); 量 sta)

if(RX_DR) {

CE = 0;

//SPI使能

// 判断是否接收到数据

// 读取状态寄存其来判断数据接收状况 (将当前状态赋值给 可位寻址变

SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);// 读 RD_RX_PLOAD中的有效

数据,存放到 RX_FIFO 缓冲器中

}

SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志

return revale; 回1

}

/***********************************************************************************************************

/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf) /*功能:发送 tx_buf中数据

/**********************************************************************************************************/

void nRF24L01_TxPacket(unsigned char * tx_buf) { CE=0;

//CE 为低,处于待机模式 I

// 将读取数据完成标志位的值返回,若没接收到数据,则返回0,接收到数据则返

revale =1;

//读取数据完成标志

SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地

23

址,这是因为发射设备中的通道0是用来接收应答信号的,通道0的接收地址(RX_ADDR_P0寄存器)和发射地址(TX_ADDR寄存器)必须相同

SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH); 的数据写入 WR_TX_PLOAD 寄存器中

// IRQ收发完成中断响应,16位CRC,主发

// 装载数据,将要发送

SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);

送,这条语句不用写是因为 NRF24L01 初始化时已经设置为发送模式。

CE=1;

//置高CE,激发数据发送

inerDelay_us(10); // CE 高电平持续时间要大于 10 us }

//************************************

************************************************************

void main(void) {

unsigned char RxBuf[32]={0}; led0=1;led1=1;

init_NRF24L01() ; // 初始化 nRF24L01,将其设定为 发射模式 // nRF24L01_TxPacket(TxBuf); // 发送 数组 TxBuf[] 中的数据

// Delay(6000); //

TxBuf[0]=1;

while(1) {

SetRX_Mode(); nRF24L01_RxPacket(RxBuf);

if(RxBuf[0])

{

led1=~led1; while(1) {

switch(RxBuf[0])

{

24

case 1:

while(RxBuf[0]==1) {

right1(16); SetRX_Mode();

nRF24L01_RxPacket(RxBuf);

}led1=~led1; break;//直行;

case 2:

while(RxBuf[0]==2) {

straightback1(); SetRX_Mode();

nRF24L01_RxPacket(RxBuf);

}led1=~led1; break;

case 3: while(RxBuf[0]==3)

{ stop(); SetRX_Mode();

nRF24L01_RxPacket(RxBuf);

}led1=~led1; break; case 4:

while(RxBuf[0]==4) {

straightforward1(); SetRX_Mode();

nRF24L01_RxPacket(RxBuf);

}led1=~led1; break;

25

case 5: while(RxBuf[0]==5)

{ left1(12); SetRX_Mode();

nRF24L01_RxPacket(RxBuf);

}led1=~led1;break; case 6: while(RxBuf[0]==6) { xunji();

//straightback1();//直退

if(FL==0&&FR==0&&FS==0) {SetRX_Mode();

nRF24L01_RxPacket(RxBuf);}

}led1=~led1;break;

}

} } }

}

4.2 LCD12864液晶显示程序

#include #include #include

#define uchar unsigned char #define uint unsigned int

sbit trig=P1^0 ; //超声波模块信号发射端口 sbit echo=P1^1; //超声波模块信号接收端口 sbit LCD_RS = P2^0; //寄存器选择输入 sbit LCD_RW = P2^1; //液晶读/写控制 sbit LCD_EN = P2^2; //液晶使能控制 sbit LCD_PSB = P2^5; //串/并方式控制

unsigned long rate;

unsigned long distance=0; float a=0,b=0,zd1=0,zd2=0;

26

float s,v,vy; uchar flag=0;

uint tt,time=0,timer=0;

uchar table1[]=\测得距离:%uchar table7[]=%uchar code tablelc[]=\小车路程%uchar code tablesdz[]=\左轮速度%uchar code tablesdy[]=\右轮速度%uchar tabledw[]=%uchar table0[]=\

void lcd_pos(uchar X,uchar Y); void get_distrance(); void dis_distance();

void delay(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } //延时子函数

void delay_50us(uint t) //延迟n倍50us { uchar j; for(;t>0;t--) for(j=9;j>0;j--); }

/***************读12864端口、位置************/ void write_com(uint com) { LCD_RS = 0; LCD_RW = 0; LCD_EN = 0; P0 = com; delay(5);

LCD_EN = 1; delay(5);

LCD_EN = 0; }

/***************写12864数据口**************/ void write_data(uint dat) { LCD_RS = 1;

27

LCD_RW = 0; LCD_EN = 0; P0 = dat; delay(5);

LCD_EN = 1; delay(5);

LCD_EN = 0; }

/******************LCD12864显示字符函数*******/ void print(uchar *str) {

while(*str!='\\0') { write_data(*str); str++; } }

/************12864显示速度函数***********/ void write_sfmsd(uint add,uint date) { uchar bai,sh,g; bai=date/100; sh=(date-bai*100)/10; g=date; write_com(add); print(table0); write_data(table7[bai]); write_data('.'); write_data(table7[sh]); write_data(table7[g]); write_data('M'); delay(5); write_com(0x97); print(tabledw); write_com(0x8F); print(tabledw); write_com(0x0c); }

/************12864显示路程函数**********/ void write_sfmlc(uint add,uint date) { uchar qian,bai,sh,g; if(date>=9999) { s=0;

28

} qian=date/1000; bai=(date-qian*1000)/100; sh=(date-qian*1000-bai*100)/10; g=date; write_com(add); write_data(table7[qian]); write_data(table7[bai]); write_data('.'); write_data(table7[sh]); write_data(table7[g]); write_data('M'); write_com(0x97); print(tabledw); write_com(0x0c); delay(5); }

/************lcd、定时器初始化函数*************/ void init() //初始化 {

TMOD=0x11; //设T0为方式1,GATE=1; TH0=0; TL0=0; TH1=(65536-50000)/256; //定时器1初始值设置(50ms) TL1=(65536-50000)%6; ET0=1; //允许T0中断 // TR0=1; //定时器0运行控制位 ET1=1; //允许T1中断 TR1=1; //开启定时器1 EA=1; //开启总中断 EX0=1; //外部中断0 IT0=1; EX1=1; //外部中断1 IT1=1;

/***********lcd12864初始化**************/ delay_50us(300); LCD_PSB = 1; //并口方式 write_com(0x30); //基本指令操作 write_com(0x0C); //显示开,关光标

write_com(0x01); //清除LCD的显示内容LCD_PSB = 1; write_com(0x80);

29

//并口方式

print(tablelc); //显示文字“小车路程” write_com(0x90); print(tablesdz); //显示文字“左轮速度” write_com(0x88); print(tablesdy); //显示文字“右轮速度” delay(5); }

/*******************LCD12864cesu主函数**************************/ void LCD12864cesu_main() { LCD_RW=0; get_distrance(); dis_distance(); }

void get_distrance() //计时,并计算测得的距离distance { rate=340; //设定速度 while(!echo); //当echo为零时等待,当echo=1时(接收到一个1的信号),到下一步 TR0=1; //开启计数 // x=a; while(echo); //当echo为1计数并等待,当echo=0时(没有接收到信号),到下一步 TR0=0; //关闭计数 v=2.1*a; //速度v vy=2.1*b; s=s+(2.1*a+2.1*b)/2; //累加共走了s厘米 write_sfmlc(0x84,s); write_sfmsd(0x94,v); write_sfmsd(0x8C,vy); a=0; b=0; //x=0;

time=TH0*256+TL0; TH0=0; TL0=0; distance=(time*rate*0.01)/20; //算出来是CM*10 毫米级 }

void dis_distance() //测量距离lcd显示

30

{

int nn; uchar dt3,dt2,dt1,dt0;

if((distance>=5000)) //超出测量范围(5m或接收不到信号超时)显示“-”;////删除了 ||flag==1 { //flag=0; write_com(0x98); print(table1); //显示文字“测量距离:” for(nn=0;nn<3;nn++) { write_data('-'); } } else { dt3=distance/1000; dt2=distance00/100; dt1=distance0/10; dt0=distance; write_com(0x98); print(table1); //显示文字“测量距离:” write_data(table7[dt3]); write_data(table7[dt2]); write_data(table7[dt1]); write_data('.'); write_data(table7[dt0]); write_data('C'); write_data('M'); delay_50us(20); } }

void exter0() interrupt 0 //INT0外部中断0.P3^2口 { a++; }

void exter1() interrupt 2 //INT1外部中断1.P3^3口 { b++; }

void time1() interrupt 3 //T0中断用来扫描数码管和计800MS启动模块 {

TH1=(65536-46083)/256; //定时器1初始值设置(50ms) TL1=(65536-46083)%6;

31

timer++; if(timer>=10) //500ms 启动一次模块 { timer=0; trig=1; _nop_(); //发射探头的持续高电平,至少8us,越高越稳定,但误差也更高 _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); trig=0; } }

/*******************主函数**************************/ void main() { init(); //初始化 while(1) { LCD12864cesu_main(); //lcd12864测速测距函数 } }

4.3 无线电发送程序

#include #include

//#include<12864cusu.c> typedef unsigned char uchar; typedef unsigned char uint;

32

//****************************************IO端口定义***************************************

sbit CE =P2^5; //工作模式控制,在CSN为低的情况下,CE协同NRF24L01的CONFIG寄存器共同决定NRF24L01的状态

sbit CSN =P2^0; //SPI片选使能,低电平使能 sbit SCK =P2^4; //SPI时钟 sbit MOSI=P2^1; //SPI串行输入 sbit MISO=P2^3; //SPI串行输出 sbit IRQ =P2^2; //中断,低电平使能

//***********************************数码管0-9编码*******************************************

uchar seg[10]={0xC0,0xCF,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //0~~9段码 //

//************************************按键********************************************** sbit KEY1=P1^1; sbit KEY2=P1^2; sbit KEY3=P1^3; sbit KEY4=P1^4; sbit KEY5=P1^5; sbit KEY6=P1^6;

//***********************************数码管位选************************************************** sbit led1=P1^6; sbit led0=P0^7;

//*********************************************NRF24L01*************************************

#define TX_ADR_WIDTH 5 // 5字节发射地址宽度 #define RX_ADR_WIDTH 5 // 5字节接收地址宽度 #define TX_PLOAD_WIDTH 32 // 20 uints TX payload #define RX_PLOAD_WIDTH 32 // 20 uints TX payload

uint const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址 uint const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址 //***************************************NRF24L01寄存器指令*******************************************************

#define READ_REG 0x00 // 读寄存器指令,读操作的寄存器的地址,设置地址 AAAAA = 00000,以后要变更地址只需在该数值上加即可

#define WRITE_REG 0x20 // 写寄存器指令,写操作的寄存器的地址,设置地址 AAAAA = 00000,以后要变更地址只需在该数值上加即可

#define RD_RX_PLOAD 0x61 // 读取接收数据指令(即读RX有效数据) #define WR_TX_PLOAD 0xA0 // 写待发数据指令(即写TX有效数据) #define FLUSH_TX 0xE1 // 冲洗发送 FIFO指令,(清除 TX FIFO 寄存器) #define FLUSH_RX 0xE2 // 冲洗接收 FIFO指令,(清除 RX FIFO 寄存器) #define REUSE_TX_PL 0xE3 // 定义重复装载数据指令

33

#define NOP 0xFF // 保留 (空操作) //*************************************SPI(nRF24L01)寄存器地址**************************************************** #define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式 #define EN_AA 0x01 // 自动应答功能设置 #define EN_RXADDR 0x02 // 可用信道设置 #define SETUP_AW 0x03 // 收发地址宽度设置 #define SETUP_RETR 0x04 // 自动重发功能设置 #define RF_CH 0x05 // 工作频率设置

#define RF_SETUP 0x06 // 发射速率、功耗功能设置 #define STATUS 0x07 // 状态寄存器 #define OBSERVE_TX 0x08 // 发送监测功能

#define CD 0x09 // 地址检测 #define RX_ADDR_P0 0x0A // 频道0接收数据地址 #define RX_ADDR_P1 0x0B // 频道1接收数据地址 #define RX_ADDR_P2 0x0C // 频道2接收数据地址 #define RX_ADDR_P3 0x0D // 频道3接收数据地址 #define RX_ADDR_P4 0x0E // 频道4接收数据地址 #define RX_ADDR_P5 0x0F // 频道5接收数据地址 #define TX_ADDR 0x10 // 发送地址寄存器

#define RX_PW_P0 0x11 // 接收频道0接收数据长度 #define RX_PW_P1 0x12 // 接收频道0接收数据长度 #define RX_PW_P2 0x13 // 接收频道0接收数据长度 #define RX_PW_P3 0x14 // 接收频道0接收数据长度 #define RX_PW_P4 0x15 // 接收频道0接收数据长度 #define RX_PW_P5 0x16 // 接收频道0接收数据长度 #define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置

//**************************************************************************************

void Delay(unsigned int s);

void inerDelay_us(unsigned char n); void init_NRF24L01(void); uint SPI_RW(uint uchar); uchar SPI_Read(uchar reg); void SetRX_Mode(void);

uint SPI_RW_Reg(uchar reg, uchar value);

uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars); uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars); unsigned char nRF24L01_RxPacket(unsigned char* rx_buf); void nRF24L01_TxPacket(unsigned char * tx_buf); //*****************************************长延时***************************************** void Delay(unsigned int s) { unsigned int i; for(i=0; i

34

for(i=0; i

//****************************************************************************************** uint bdata sta; //状态标志 sbit RX_DR =sta^6; sbit TX_DS =sta^5; sbit MAX_RT =sta^4;

/****************************************************************************************** /*延时函数

/******************************************************************************************/

void inerDelay_us(unsigned char n) { for(;n>0;n--) _nop_(); }

//****************************************************************************************

/*NRF24L01初始化

//***************************************************************************************/

void init_NRF24L01(void) {

inerDelay_us(100);

CE=0; // CE 为低,处于待机模式 I CSN=1; // 不使能SPI片选信号 SCK=0; // Spi clock line init high SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // 写本地地址 SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); // 写接收端地址 SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 频道0自动 ACK应答允许 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 允许接收地址只有频道0,如果需要多频道可以参考Page21 SPI_RW_Reg(WRITE_REG + RF_CH, 0); // 设置信道工作为2.4GHZ,收发必须一致 SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); //设置发射速率为1MHZ,发射功率为最大值0dB SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断响应,16位CRC,主发送

35

}

/****************************************************************************************************

/*函数:uint SPI_RW(uint uchar) /*功能:NRF24L01的SPI写时序

/****************************************************************************************************/ uint SPI_RW(uint uchar) { uint bit_ctr;

for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit { MOSI = (uchar & 0x80); // 将数据 uchar 的最高位赋值给 SPI数据输出脚 uchar = (uchar << 1); // 数据 uchar 左移一位 SCK = 1; /* MOSI上的数据在时钟上升沿发送,注意:时钟上升沿到来时,将数据 uchar的最高位移入MOSI, 同时,从设备的寄存器也将其当前存放的数据的最高位移入MISO, 紧接着在SCK的下降沿的时候从设备的 MOSI 接收到了主机发送过来的数据,同时主机也接收到了从设备发送过来的数据. */ uchar |= MISO; // 将 MISO中的数据保存到数据 uchar 的最低位 SCK = 0; // 时钟下降沿的时候将 MISO 上的电平存放到数据 uchar 的最低位 }

return(uchar); // 将从机寄存器中的数据返回 }

/****************************************************************************************************

/*函数:uchar SPI_Read(uchar reg) /*功能:NRF24L01的SPI读时序

/****************************************************************************************************/ uchar SPI_Read(uchar reg) { uchar reg_val; CSN = 0; // SPI片选信号,低电平使能,开启SPI数据传输 SPI_RW(reg); // 通过地址 reg 选择所要读的寄存器 reg_val = SPI_RW(0); // 读取所选寄存器当前存储的数据,将读到的值赋值给变量 reg_val 。 CSN = 1; // 禁止SPI数据传输 return(reg_val); // 将读到的寄存器的数据返回

36

}

/****************************************************************************************************/

/*功能:NRF24L01读写寄存器函数

/****************************************************************************************************/

uint SPI_RW_Reg(uchar reg, uchar value) { uint status; CSN = 0; // SPI片选信号,低电平使能,开启SPI数据传输 status = SPI_RW(reg); // 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的状态,赋值给变量 status SPI_RW(value); // 向所选的寄存器中写入数据 CSN = 1; // 禁止SPI数据传输 return(status); // 将从寄存器读到的状态数据返回 }

/****************************************************************************************************/

/*函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars)

/*功能: 用于读数据,reg:为寄存器地址,pBuf:为待读出数据地址,uchars:读出数据的个数

/****************************************************************************************************/

uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) { uint status,uchar_ctr; CSN = 0; // SPI片选信号,低电平使能,开启SPI数据传输 status = SPI_RW(reg); // 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的状态,赋值给变量 status for(uchar_ctr=0;uchar_ctr

/*********************************************************************************************************

/*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)

/*功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数 /*****************************************************************************

37

****************************/

uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) { uint status,uchar_ctr; CSN = 0; //SPI使能 status = SPI_RW(reg); // 通过地址 reg 选择所要读写的寄存器,并读取寄存器当前的状态,赋值给变量 status for(uchar_ctr=0; uchar_ctr

/****************************************************************************************************/ /*函数:void SetRX_Mode(void) /*功能:数据接收配置

/****************************************************************************************************/ void SetRX_Mode(void) { CE=0; // CE 为低,处于待机模式 I SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // IRQ收发完成中断响应,16位CRC ,主接收 CE = 1; // 接收模式由设置 CE 为高来启动 inerDelay_us(130); // 130 us 后 nRF24L01 开始检测空中信息。 }

/******************************************************************************************************/

/*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf) /*功能:数据读取后放入rx_buf接收缓冲区中

/******************************************************************************************************/

unsigned char nRF24L01_RxPacket(unsigned char* rx_buf) {

unsigned char revale=0; sta=SPI_Read(STATUS); // 读取状态寄存其来判断数据接收状况 (将当前状态赋值给 可位寻址变量 sta) if(RX_DR) // 判断是否接收到数据 { CE = 0; //SPI使能 SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);// 读 RD_RX_PLOAD中的有效数据,存放到 RX_FIFO 缓冲器中 revale =1; //读取数据完成标志

38

} SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志 return revale; // 将读取数据完成标志位的值返回,若没接收到数据,则返回0,接收到数据则返回1 }

/***********************************************************************************************************

/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf) /*功能:发送 tx_buf中数据

/**********************************************************************************************************/

void nRF24L01_TxPacket(unsigned char * tx_buf) { CE=0; //CE 为低,处于待机模式 I SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地址,这是因为发射设备中的通道0是用来接收应答信号的,通道0的接收地址(RX_ADDR_P0寄存器)和发射地址(TX_ADDR寄存器)必须相同 SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH); // 装载数据,将要发送的数据写入 WR_TX_PLOAD 寄存器中 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断响应,16位CRC,主发送,这条语句不用写是因为 NRF24L01 初始化时已经设置为发送模式。 CE=1; //置高CE,激发数据发送 inerDelay_us(10); // CE 高电平持续时间要大于 10 us }

//************************************主函数************************************************************ void main(void) { unsigned char TxBuf[32]={0}; led0=0;led1=1;

init_NRF24L01() ; // 初始化 nRF24L01,将其设定为 发射模式 // nRF24L01_TxPacket(TxBuf); // 发送 数组 TxBuf[] 中的数据 // Delay(6000); // TxBuf[0]=1; while(1) { if(KEY1==0) {

Delay(1000); if(KEY1==0) {

TxBuf[0]=1; //Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据

39

Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } if(KEY2==0) {

Delay(1000); if(KEY2==0) {TxBuf[0]=2; //Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据 Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } if(KEY3==0) { Delay(1000); if(KEY3==0){ TxBuf[0]=3; Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据 Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } if(KEY4==0) { Delay(1000); if(KEY4==0){ TxBuf[0]=4; Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据

40

Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } if(KEY5==0) { Delay(1000); if(KEY5==0) { TxBuf[0]=5; Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据 Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } if(KEY6==0) { Delay(1000); if(KEY6==0){ TxBuf[0]=6; Delay(6000); nRF24L01_TxPacket(TxBuf); // 发送 数组TxBuf[] 中的数据 Delay(2000); SPI_RW_Reg(WRITE_REG+STATUS,0XFF); Delay(600); TxBuf[0]=0; led0=~led0; } } } }

总结

在这次设计中,将C语言和单片机等已学的课程进行一次在学习的良好机会。通过这次学习,无疑进一步提高了自己的专业技能,为今后踏入社会,在该领域上发挥自己的特长,以及就业打下了坚实的基础。通过本次设计,使我们对大学期间所学的课程有了针对

41

性、综合性、系统性的了解,并且提高了自己的分析的能力

本系统的设计过程并非一帆风顺,刚开始接触这个课题时,心中并没有一个清晰的框架,通过翻看手册与查阅相关资料后,我渐渐有了设计的思路,对于系统所要用到的元器件类型也有了一定程度的了解。

附录1 PCB原理图

附录1-1 电机原理图

附录1-2 循迹PCB

42

附录1-3 无线电PCB

附录1-4 无线电控制板

43

44

附录1-5 LCD12864显示PCB图

45

附录2 实物图

46

本文来源:https://www.bwwdw.com/article/phdp.html

Top