简介
本项目的主要功能是通过定义星火1号开发板上相应IO的电平状态,调试智能手机与星火一号开发板之间的WIFI通讯,从而通过手机输入相关的命令,实现对麦克纳姆轮四驱小车的移动控制(如下图所示)
硬件介绍
开发板
使用 RT-Thread 星火 1 号 开发板开发本项目,各模块在开发板中位置如图所示
RSAPBEERY CONN
如上图所示,开发板RSAPBEERY CONN中各引脚与对应L298N中输入输出口的对应关系
RW007 WIFI
供电方案
八节5号电池串联提供12V电压(两个L298N,两个电池盒共计16个电池):优点是供电较为稳定其,缺点是其重量过大,可能导致小车速度下降。
充电宝3.6V+升压模块升至12V:利用可调输出电压的升压模块可以输出12V电压供L298N使用。但是因为充电宝、升压模块不稳定,其无法稳定输出12V电压,故而舍弃此方案。
接线
接线如上图所示,各个端口所对应具体情况如下:
电源线:+12V接升压模块OUT+
GND接升压模块OUT-
GND接开发板GND
+5V接开发板+5V
逻辑控制
对于Motor_A(小车前轮的两个电机)和Motor_B(小车前轮的两个电机)的控制逻辑如下,其中本项目使用的是PIN,因此无需考虑PWM
杜邦线
红色:通常用于连接电源正极或高电平信号。
黑色:通常用于连接电源负极或地线。
蓝色、绿色、黄色等其他颜色:通常用于连接数据线或其他信号线,具体用途取决于具体的威廉希尔官方网站 设计。
小车装配
第一层:电池盒及电池
第二层:星火1号开发板
第三层:L298N电机驱动模块、轮子、电机
软件介绍
WIFI 完成初始化
static int i = 0;
int result = RT_EOK;
struct rt_wlan_info info;
rt_thread_mdelay(500);
热点扫描
/* 执行扫描 */
rt_sem_init(&scan_done,"scan_done",0,RT_IPC_FLAG_FIFO);
rt_wlan_register_event_handler(RT_WLAN_EVT_SCAN_REPORT, wlan_scan_report_hander,&i);
rt_wlan_register_event_handler(RT_WLAN_EVT_SCAN_DONE, wlan_scan_done_hander,RT_NULL);
if(rt_wlan_scan() == RT_EOK)
{
LOG_D("the scan is started... ");
}else
{
LOG_E("scan failed");
}
/*等待扫描完毕 /
rt_sem_take(&scan_done,RT_WAITING_FOREVER);
Join 网络
/ 热点连接 /
LOG_D("start to connect ap ...");
rt_sem_init(&net_ready, "net_ready", 0, RT_IPC_FLAG_FIFO);
/ 注册 wlan ready 回调函数 /
rt_wlan_register_event_handler(RT_WLAN_EVT_READY, wlan_ready_handler, RT_NULL);
/ 注册 wlan 断开回调函数 /
rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED, wlan_station_disconnect_handler, RT_NULL);
/ 同步连接热点 /
result = rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);
if (result == RT_EOK)
{
rt_memset(&info, 0, sizeof(struct rt_wlan_info));
/ 获取当前连接热点信息 /
rt_wlan_get_info(&info);
LOG_D("station information:");
print_wlan_information(&info,0);
/ 等待成功获取 IP /
result = rt_sem_take(&net_ready, NET_READY_TIME_OUT);
if (result == RT_EOK)
{
LOG_D("networking ready!");
msh_exec("ifconfig", rt_strlen("ifconfig"));
}
else
{
LOG_D("wait ip got timeout!");
}
/ 回收资源 */
rt_wlan_unregister_event_handler(RT_WLAN_EVT_READY);
rt_sem_detach(&net_ready);
}
else
{
LOG_E("The AP(%s) is connect failed!", WLAN_SSID);
}
rt_thread_mdelay(5000);
LOG_D("ready to disconect from ap ...");
rt_wlan_disconnect();
自动连接
LOG_D("start to autoconnect ...");
wifi_autoconnect();
串口连接模块
使用sscom5发送数据,rt-studio平台接收数据,数据作为参数传入到电机控制模块
static const char send_data[] = "This is TCP Client from RT-Thread."; /* 发送用到的数据 */
char recv_data;
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
/ 线程2的入口函数 */
static void thread2_entry(void *parameter)
{
int ret;
struct hostent *host;
int sock, bytes_received;
struct sockaddr_in server_addr;
const char url;
int port;
url = "192.168.43.96";
port = 8800;
/ 通过函数入口参数url获得host地址(如果是域名,会做域名解析) /
host = gethostbyname(url);
/ 分配用于存放接收数据的缓冲 /
recv_data = rt_malloc(BUFSZ);
if (recv_data == RT_NULL)
{
rt_kprintf("No memoryn");
return;
}
/ 创建一个socket,类型是SOCKET_STREAM,TCP类型 /
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/ 创建socket失败 /
rt_kprintf("Socket errorn");
/ 释放接收缓冲 /
rt_free(recv_data);
return;
}
/ 初始化预连接的服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr )host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/ 连接到服务端 */
if (connect(sock, (struct sockaddr )&server_addr, sizeof(struct sockaddr)) == -1)
{
/ 连接失败 /
rt_kprintf("Connect fail!n");
closesocket(sock);
/ 释放接收缓冲 /
rt_free(recv_data);
return;
}
while (1)
{
/ 从sock连接中接收最大BUFSZ - 1字节数据 /
bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
if (bytes_received < 0)
{
/ 接收失败,关闭这个连接 /
closesocket(sock);
rt_kprintf("nreceived error,close the socket.rn");
/ 释放接收缓冲 /
rt_free(recv_data);
break;
}
else if (bytes_received == 0)
{
/ 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接 /
closesocket(sock);
rt_kprintf("nreceived error,close the socket.rn");
/ 释放接收缓冲 /
rt_free(recv_data);
break;
}
/ 有接收到数据,把末端清零 /
recv_data[bytes_received] = '�';
if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0)
{
/ 如果是首字母是q或Q,关闭这个连接 /
closesocket(sock);
rt_kprintf("n got a 'q' or 'Q',close the socket.rn");
/ 释放接收缓冲 /
rt_free(recv_data);
break;
}
else
{
/ 在控制终端显示收到的数据 /
rt_kprintf("nReceived data = %s ", recv_data);
}
/ 发送数据到sock连接 /
ret = send(sock, send_data, strlen(send_data), 0);
if (ret < 0)
{
/ 接收失败,关闭这个连接 /
closesocket(sock);
rt_kprintf("nsend error,close the socket.rn");
rt_free(recv_data);
break;
}
else if (ret == 0)
{
/ 打印send函数返回值为0的警告信息 /
rt_kprintf("n Send warning,send function return 0.rn");
}
}
return;
rt_thread_mdelay(500);
}
/ 线程2 /
int thread2_sample(void)
{
/ 创建线程2,名称是thread2,入口是thread2_entry /
tid2 = rt_thread_create("thread2",
thread2_entry, RT_NULL,
2048,
THREAD_PRIORITY, THREAD_TIMESLICE);
/ 如果获得线程控制块,启动这个线程 /
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
return 0;
}
MSH_CMD_EXPORT(thread2_sample, thread sample);
开发板IO引脚定义
/ 配置 控制电机引脚(L2980N1) /
#define PIN_Motor_PA0 GET_PIN(A, 0) // PA0 : define --> Motor1
#define PIN_Motor_PA8 GET_PIN(A, 8) // PA8 : define --> Motor1
#define PIN_Motor_PB15 GET_PIN(B, 15) // PB15: define --> Motor2
#define PIN_Motor_PB14 GET_PIN(B, 14) // PB14: define --> Motor2
/ 配置 控制电机引脚(L2980N2) /
#define PIN_Motor_PB2 GET_PIN(B, 2) // PB2 : define --> Motor3
#define PIN_Motor_PG6 GET_PIN(G, 6) // PG6 : define --> Motor3
#define PIN_Motor_PG7 GET_PIN(G, 7) // PG7 : define --> Motor4
#define PIN_Motor_PD7 GET_PIN(D, 7) // PD7 : define --> Motor4
/ 定义电机引脚宏 */
#define PIN_Motor1_A PIN_Motor_PA0
#define PIN_Motor1_B PIN_Motor_PA8
#define PIN_Motor2_A PIN_Motor_PB15
#define PIN_Motor2_B PIN_Motor_PB14
#define PIN_Motor3_A PIN_Motor_PB2
#define PIN_Motor3_B PIN_Motor_PG6
#define PIN_Motor4_A PIN_Motor_PG7
#define PIN_Motor4_B PIN_Motor_PD7
小车行驶控制
通过设置对应IO引脚的高低电平,可以控制电机的转动和停止,从而控制小车的前进,后退,左移和右移
/* 控制小车前进 /
void motor_forward(void) {
// 向前加速1s
rt_pin_write(PIN_Motor1_A, PIN_HIGH);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_HIGH);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_HIGH);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_HIGH);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
rt_thread_mdelay(1000);
//停下
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
}
/ 控制小车后退 /
void motor_backward(void) {
// 向后加速1s
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_HIGH);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_HIGH);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_HIGH);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_HIGH);
rt_thread_mdelay(1000);
//停下
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
}
/ 控制小车左走*/
void motor_left(void) {
// 向左加速0.5s
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_HIGH);
rt_pin_write(PIN_Motor2_A, PIN_HIGH);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_HIGH);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_HIGH);
rt_thread_mdelay(1000);
//停下
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
}
/* 控制小车右走 /
void motor_right(void) {
// 向右加速0.5s
rt_pin_write(PIN_Motor1_A, PIN_HIGH);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_HIGH);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_HIGH);
rt_pin_write(PIN_Motor4_A, PIN_HIGH);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
rt_thread_mdelay(1000);
//停下
rt_pin_write(PIN_Motor1_A, PIN_LOW);
rt_pin_write(PIN_Motor1_B, PIN_LOW);
rt_pin_write(PIN_Motor2_A, PIN_LOW);
rt_pin_write(PIN_Motor2_B, PIN_LOW);
rt_pin_write(PIN_Motor3_A, PIN_LOW);
rt_pin_write(PIN_Motor3_B, PIN_LOW);
rt_pin_write(PIN_Motor4_A, PIN_LOW);
rt_pin_write(PIN_Motor4_B, PIN_LOW);
}
/ 线程1的入口函数 /
static void thread1_entry(void parameter)
{
rt_pin_mode(PIN_Motor_PA0, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PA8, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PB15, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PB14, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PB2, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PG6, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PG7, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_Motor_PD7, PIN_MODE_OUTPUT);
while (1)
{
/ 线程1采用低优先级运行,一直打印计数值 /
if (strcmp(recv_data, "a") == 0) {
motor_forward();
// rt_kprintf(" data:%s rn",recv_data);
rt_memset(recv_data, 0, strlen(recv_data));
}
else if (strcmp(recv_data, "b") == 0) {
motor_backward();
rt_memset(recv_data, 0, strlen(recv_data));
}
else if (strcmp(recv_data, "c") == 0) {
motor_left();
rt_memset(recv_data, 0, strlen(recv_data));
}
else if (strcmp(recv_data, "d") == 0) {
motor_right();
rt_memset(recv_data, 0, strlen(recv_data));
}
rt_thread_mdelay(500);
}
}
/ 线程1 /
int thread1_sample(void)
{
/ 创建线程1,名称是thread1,入口是thread1_entry /
tid1 = rt_thread_create("thread1",
thread1_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 /
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
return 0;
}
/ 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread1_sample, thread sample);
运行结果
最后,在完成上面步骤后,我们通过命令行执行一下代码(如下图)即可完成小车与控制设备的WIFI连接(下面代码以小车与智能手机的WIFI连接为例)
评论
查看更多