第一次在CSDN分享自己的课程作业,使用课程实现的内容MSP432和TI机器人套装采用红外传感器实时检测距离,然后通过串口传输距离数据ESP8266模块再次使用8266WIFI将数据传输到计算机端,同时,汽车可以根据实时距离控制自己的运动,使自己保持在安全范围内。
目录
- 软硬件准备
-
- 硬件准备
- 软件准备
- MSP432编程
- ESP8266编程
- 调试
-
- MSP432和ESP8266的连接
- 结尾
软硬件准备
硬件准备
-
主板:MSP432
-
WIFI模块:ESP8266
-
红外传感器模块:SHARP 2Y0A21
(注:TI机器人套装包含MSP432和红外传感器模块。
软件准备
-
Code Compose Studio 9.1(MSP开发环境432)
-
AiThinkerIDE_V0.5(ESP开发环境8266)
(注:这两个软件的安装可以在网上找到很多教程。
MSP432编程
- 头文件
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
导入文件需要安装432SDK,可以在TI官网下载:SDK
- 定义红外传感器的状态
// IR Sensor Wall Classification enum scenario { Error = 0, //状态错误 CenterRight = 1, //正确状态 Straight = 2, ///前进状态 Back = 3 //后退状态 };
- 定义其它参数
#define CENTERMAX 250 // max distance to wall in the front #define CENTERMIN 150 // min distance to wall in the front uint8_t TXData1 = 0; //uart send data1 uint8_t TXData2 = 0; //uart send data2 uint8_t RXData = 0; //uart receive data
安全区设置范围为15cm-25cm,并定义2个8位的串口发送数据和1个8位的串口接收数据。
- 串口参数配置
const eUSCI_UART_ConfigV1 uartConfig = { EUSCI_A_UART_CLOCKSOURCE_SMCLK, // SMCLK Clock Source 26, // BRDIV = 26 0, // UCxBRF = 0 111, // UCxBRS = 111 EUSCI_A_UART_NO_PARITY, // No Parity EUSCI_A_UART_LSB_FIRST, // LSB First EUSCI_A_UART_ONE_STOP_BIT, // One stop bit EUSCI_A_UART_MODE, // UART mode EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION, // Oversampling EUSCI_A_UART_8_BIT_LEN // 8 bit data length };
波特率为115200,使用48MHz时钟频率,低位第一,1个停止位,0个验证位,1个数据长度8bit。(应该和ESP8266串口参数匹配)
BRDIV、UCxBRF、UCxBRS根据设定的波特率和时钟频率计算这三个参数,官方提供了网页版的计算工具: 计算工具
- 串口初始化
// initialize clock //clock_init_48MHz(); /* Selecting P1.2 and P1.3 in UART mode*/ MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION); /* Setting DCO to 48MHz (upping Vcore) */ FlashCtl_setWaitState(FLASH_BANK0, 1); FlashCtl_setWaitState(FLASH_BANK1, 1); MAP_PCM_setCoreVoltageLevel(PCM_VCORE1); CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48); /* Configuring UART Module */ MAP_UART_initModule(EUSCI_A0_BASE, &uartConfig); /* Enable UART module */ MAP_UART_enableModule(EUSCI_A0_BASE); /* Enabling interrupts */ MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); MAP_Interrupt_enableInterrupt(INT_EUSCIA0); MAP_Interrupt_enableSleepOnIsrExit();
设置P1.2引脚为TXD和P1.3引脚为RXD,DCO=将寄存器分配到串口,然后启动串口和中断。
- 读取红外传感器的数据,并用串口发送ESP8266
// the sending data is 8bit or 16bit if(ir_data.center<255) { TXData1 = ir_data.center&0xffffff; TXData2 = 0; MAP_UART_transmitData(EUSCI_A0_BASE, TXData1); MAP_UART_transmitData(EUSCI_A0_BASE, TXData2); } else { TXData1 = ir_data.center>>8&0xffff; TXData2 = ir_data.center&0xffffff; MAP_UART_transmitData(EUSCI_A0_BASE, TXData1); MAP_UART_transmitData(EUSCI_A0_BASE, TXData2); } MAP_Interrupt_enableSleepOnIsrExit(); MAP_PCM_gotoLPM0InterruptSafe();
若距离小于255mm,则只需1个8bit的TXData,因为我设置最高只能检测到8000mm,所以最多只需要2个8bit的TXData,发送数据后,进入等待中断的状态。
- 中断函数
/* EUSCI A0 UART ISR - receive data from ESP8266 */ void EUSCIA0_IRQHandler(void) { uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_BASE); if(statu & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG)
{
RXData = MAP_UART_receiveData(EUSCI_A0_BASE);
MAP_Interrupt_disableSleepOnIsrExit();
}
}
当ESP8266发送来应答数据时,就进入到了串口中断函数中,接收完应答数据后,程序退出中断,开始控制小车运动。
- 根据距离判断小车状态
scenario_t classify(uint32_t Left, uint32_t Center, uint32_t Right){
if((Center<0)||(Center>800)) return Error;
if(Center>=CENTERMAX){
return Straight;
}
if(Center<=CENTERMIN){
return Back;
}
if(Center<CENTERMAX && Center>CENTERMIN){
return CenterRight;
}
return Error; // should happen
}
IR_class = classify(ir_data.left, ir_data.center, ir_data.right);
- 根据小车的状态控制小车运动
switch(IR_class)
{
case Straight:
// Move forward with both motors equal
launchpad_output(LP_GREEN);
drive_straight();
break;
case Back:
launchpad_output(LP_BLUE);
drive_back();
break;
case CenterRight:
launchpad_output(LP_RED);
motor_stop();
default:
// This case should not occur, but is included to indicate errors
launchpad_output(LP_PINK);
motor_stop();
break;
}
小车根据对应的4个状态作出反应:①前进状态:亮绿灯,小车前进;②后退状态:亮蓝灯,小车后退;③正确状态:亮红灯,小车静止;④错误状态:亮粉灯,小车静止。
- 以上步骤的6、8、9均包含在循环中,小车可以不停地判断距离,然后移动,使自己处于安全范围中。
ESP8266编程
- 相关定义
// 头文件引用
//==================================================================================
#include "user_config.h" // 用户配置
#include "c_types.h" // 变量类型
#include "eagle_soc.h" // GPIO函数、宏定义
#include "ets_sys.h" // 回调函数
#include "os_type.h" // os_XXX
#include "osapi.h" // os_XXX、软件定时器
#include "ip_addr.h" // 被"espconn.h"使用。在"espconn.h"开头#include"ip_addr.h"或#include"ip_addr.h"放在"espconn.h"之前
#include "espconn.h" // TCP/UDP接口
#include "user_interface.h" // 系统接口、system_param_xxx接口、WIFI、RateContro
#include "mem.h" // 内存申请等函数
#include "driver/uart.h" //串口
#include "driver/oled.h" //OLED
//==================================================================================
// 宏定义
//==================================================================================
#define ProjectName "MSP432_ESP8266_WIFI_TCP" // 工程名宏定义
#define ESP8266_AP_SSID "ESP8266_WHX" // 创建的WIFI名
#define ESP8266_AP_PASS "123456" // 创建的WIFI密码
#define LED_ON GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0) // LED亮
#define LED_OFF GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1) // LED灭
//==================================================================================
// 全局变量
//==================================================================================
uint8_t TCPSendData[4]={0}; // TCP发送的数据
os_timer_t OS_Timer_1; // 定义软件定时器,用于等待WIFI连接
os_timer_t OS_Timer_2; // 定义软件定时器,用于定时发送数据
struct espconn ST_NetCon; // 网络连接结构体
struct espconn * dataarg; // TCP发送数据的指针
//==================================================================================
以下编程涉及到SDK编程,需要提前到乐鑫官网下载esp8266的SDK,否则无法导入头文件"driver/uart.h"和使用串口相关函数。 ESP8266_NONOS_SDK-3.0【提取码:b4y6】
- 初始化设置
void ICACHE_FLASH_ATTR user_init(void)
{
partition_item_t partition_item;
if (!system_partition_get_item(SYSTEM_PARTITION_CUSTOMER_PRIV_PARAM, &partition_item)) {
os_printf("Get partition information fail\n");
}
uart_init(115200,115200); //初始化串口波特率为115200
os_delay_us(10000); // 等待串口稳定
// OLED显示初始化
//------------------------------------------------------------
OLED_Init(); // OLED初始化
OLED_ShowString(0,0,"ESP8266 = Client"); // ESP8266模式
OLED_ShowString(0,2,"IP:"); // ESP8266_IP地址
OLED_ShowString(0,4,"Remote = Server"); // 远端主机模式
OLED_ShowString(0,6,"IP:"); // 远端主机IP地址
//------------------------------------------------------------
LED_Init_JX(); // LED初始化
ESP8266_AP_Init_JX(); // 初始化ESP8266_AP模式
OS_Timer_1_Init_JX(10000,0); // 10秒定时(一次)
}
- AP模式的初始化
void ICACHE_FLASH_ATTR ESP8266_AP_Init_JX()
{
struct softap_config AP_Config; // AP参数结构体
wifi_set_opmode(0x02); // 设置为AP模式,并保存到Flash
// 结构体赋值(注意:【服务集标识符/密码】须设为字符串形式)
//--------------------------------------------------------------------------------------
os_memset(&AP_Config, 0, sizeof(struct softap_config)); // AP参数结构体 = 0
os_strcpy(AP_Config.ssid,ESP8266_AP_SSID); // 设置SSID(将字符串复制到ssid数组)
os_strcpy(AP_Config.password,ESP8266_AP_PASS); // 设置密码(将字符串复制到password数组)
AP_Config.ssid_len=os_strlen(ESP8266_AP_SSID); // 设置ssid长度(和SSID的长度一致)
AP_Config.channel=1; // 通道号1~13
AP_Config.authmode=AUTH_WPA2_PSK; // 设置加密模式
AP_Config.ssid_hidden=0; // 不隐藏SSID
AP_Config.max_connection=4; // 最大连接数
AP_Config.beacon_interval=100; // 信标间隔时槽100~60000 ms
wifi_softap_set_config(&AP_Config); // 设置soft-AP,并保存到Flash
}
在AP模式初始化函数中,对AP模式的各项参数进行配置,包括SSID,密码,加密模式和最大连接数等。初始化完成后,启动一个10秒的定时器,用于等待WIFI连接。
- 定时器1的回调函数
void ICACHE_FLASH_ATTR OS_Timer_1_cb(void)
{
struct ip_info ST_ESP8266_IP; // IP信息结构体
u8 ESP8266_IP[4]; // 点分十进制形式保存IP
wifi_get_ip_info(SOFTAP_IF,&ST_ESP8266_IP); // 查询AP模式下ESP8266的IP地址
if(ST_ESP8266_IP.ip.addr!=0 ) // ESP8266成功获取到IP地址
{
ESP8266_IP[0] = ST_ESP8266_IP.ip.addr; // 点分十进制IP的第一个数 <==> addr低八位
ESP8266_IP[1] = ST_ESP8266_IP.ip.addr>>8; // 点分十进制IP的第二个数 <==> addr次低八位
ESP8266_IP[2] = ST_ESP8266_IP.ip.addr>>16; // 点分十进制IP的第三个数 <==> addr次高八位
ESP8266_IP[3] = ST_ESP8266_IP.ip.addr>>24; // 点分十进制IP的第四个数 <==> addr高八位
// 显示ESP8266的IP地址
//-----------------------------------------------------------------------------------------------
//os_printf("ESP8266_IP = %d.%d.%d.%d\n",ESP8266_IP[0],ESP8266_IP[1],ESP8266_IP[2],ESP8266_IP[3]);
OLED_ShowIP(24,2,ESP8266_IP); // OLED显示ESP8266的IP地址
//-----------------------------------------------------------------------------------------------
os_timer_disarm(&OS_Timer_1); // 关闭定时器
ESP8266_NetCon_Init_JX(); // 初始化网络连接(TCP通信)
}
}
定时器1的10s定时一到就进入到该回调函数中。
- 初始化网络连接(TCP通信)
void ICACHE_FLASH_ATTR ESP8266_NetCon_Init_JX()
{
// 结构体赋值
//--------------------------------------------------------------------------
ST_NetCon.type = ESPCONN_TCP ; // 设置为TCP协议
ST_NetCon.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); // 开辟内存
// 此处需要设置目标IP/端口(ESP8266作为Client,需要预先知道Server的IP/端口)
//-------------------------------------------------------------------------
ST_NetCon.proto.tcp->local_port = 8266 ; // 设置本地端口
ST_NetCon.proto.tcp->remote_port = 8888; // 设置目标端口
ST_NetCon.proto.tcp->remote_ip[0] = 192; // 设置目标IP地址
ST_NetCon.proto.tcp->remote_ip[1] = 168;
ST_NetCon.proto.tcp->remote_ip[2] = 4;
ST_NetCon.proto.tcp->remote_ip[3] = 2;
// 注册连接成功回调函数、异常断开回调函数
//--------------------------------------------------------------------------------------------------
espconn_regist_connectcb(&ST_NetCon, ESP8266_TCP_Connect_Cb_JX); // 注册TCP连接成功建立的回调函数
espconn_regist_reconcb(&ST_NetCon, ESP8266_TCP_Break_Cb_JX); // 注册TCP连接异常断开的回调函数
// 连接 TCP server
//----------------------------------------------------------
espconn_connect(&ST_NetCon); // 连接TCP-server
}
在该函数中,设置网络通信协议方式为TCP,同时设置本地端口号和目标服务器的端口号、IP地址,设置完成后,注册TCP连接成功和异常断开的回调函数,然后就可以启动TCP连接服务。
- TCP连接成功
void ICACHE_FLASH_ATTR ESP8266_TCP_Connect_Cb_JX(void *arg)
{
dataarg = (struct espconn *)arg;
espconn_regist_sentcb((struct espconn *)arg, ESP8266_WIFI_Send_Cb_JX); // 注册网络数据发送成功的回调函数
espconn_regist_recvcb((struct espconn *)arg, ESP8266_WIFI_Recv_Cb_JX); // 注册网络数据接收成功的回调函数
espconn_regist_disconcb((struct espconn *)arg,ESP8266_TCP_Disconnect_Cb_JX); // 注册成功断开TCP连接的回调函数
OS_Timer_2_Init_JX(1000,1);
}
如果TCP连接成功,就启动定时器2,这是一个重复的1s定时。
- 定时器2的回调函数
void ICACHE_FLASH_ATTR OS_Timer_2_cb(void)
{
uint8 idx=0;
//数据处理
if(uartRxBuffer[1]==0x00)
{
change_to_dec0(0); //十六进制转十进制(红外数据只有8位时)
}
else
{
change_to_dec1(1); //十六进制转十进制(红外数据有16位时)
}
espconn_send(dataarg,&TCPSendData[0],os_strlen(TCPSendData));//发送TCP数据
//清空TCP发送数据缓存
for(idx=0;idx<4;idx++)
{
TCPSendData[idx]=0;
idx++;
}
//清空串口接收数据缓存
for(idx=0;idx<32;idx++)
{
uartRxBuffer[idx]=0;
idx++;
}
//向MSP432回复1,表示已成功接收到数据
os_printf("1");
}
在WIFI发送数据前,要先对串口接收的数据进行处理,否则服务器端接收到的数据无法正常阅读。
- 距离数据类型的转换
void change_to_dec0(uint8 idx)
{
int a=0;
if((uartRxBuffer[idx]&0x01)==0x01)
a+=1;
if((uartRxBuffer[idx]&0x02)==0x02)
a+=2;
if((uartRxBuffer[idx]&0x04)==0x04)
a+=4;
if((uartRxBuffer[idx]&0x08)==0x08)
a+=8;
if((uartRxBuffer[idx]&0x10)==0x10)
a+=16;
if((uartRxBuffer[idx]&0x20)==0x20)
a+=32;
if((uartRxBuffer[idx]&0x40)==0x40)
a+=64;
if((uartRxBuffer[idx]&0x80)==0x80)
a+=128;
int a1 = a%10;
int a2 = ((a-a1)%100)/10;
int a3 = a/100;
change_to_char(0,a3); //十进制ASCII编码
change_to_char(1,a2);
change_to_char(2,a1);
}
void change_to_char(uint8 idx,int a)
{
if(a==0)
TCPSendData[idx]=0x30;
if(a==1)
TCPSendData[idx]=0x31;
if(a==2)
TCPSendData[idx]=0x32;
if(a==3)
TCPSendData[idx]=0x33;
if(a==4)
TCPSendData[idx]=0x34;
if(a==5)
TCPSendData[idx]=0x35;
if(a==6)
TCPSendData[idx]=0x36;
if(a==7)
TCPSendData[idx]=0x37;
if(a==8)
TCPSendData[idx]=0x38;
if(a==9)
TCPSendData[idx]=0x39;
}
红外传感器测到的数据,将以十六进制的形式通过串口发送给ESP8266,如果不进行数据类型的转换,最后在服务器端看到的将是十六进制的数据,不能直观展示距离大小。
注:16位数据的转换与8位的类似,这里不再贴出。
- 串口接收函数的修改
for(idx=0;idx<fifo_len;idx++) {
d_tmp = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
uartRxBuffer[idx] = d_tmp;
}
原本的串口接收函数uart_recvTask,一次接收一位值,并且接收一个就把该值再通过串口发送出去,我这里使用一个数组将接收到值储存起来,方便后续使用。
调试
MSP432和ESP8266的连接
结尾
这个课设由本人独立完成,大部分参考了官方例程和一些网络教程,如果有问题,欢迎大家来交流!因为具体代码略长不能全部贴出,完整代码我已上传至下方连接:使用MSP432-ESP8266实现小车红外测距和数据传输