天津营销网站建设公司哪家好,市场营销平台,网站开发是前端开发吗,crack wordpress本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写#xff0c;需要的同学可以在这里获取#xff1a; https://item.taobao.com/item.htm?id728461040949
配套资料获取#xff1a;https://renesas-docs.100ask.net
瑞萨MCU零基础入门系列教程汇总#xff1a; ht…本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写需要的同学可以在这里获取 https://item.taobao.com/item.htm?id728461040949
配套资料获取https://renesas-docs.100ask.net
瑞萨MCU零基础入门系列教程汇总 https://blog.csdn.net/qq_35181236/article/details/132779862 第31章 WIFI蓝牙模块驱动实验
本章目标
了解WiFi蓝牙芯片W800的通信协议学会使用串口收发AT指令实现开发板联网
31.1 W800出厂固件烧写
烧写W800的固件时需要使用X/Y modem串口协议。因而需要支持X/Y modem协议的串口工具本书使用Xshell。
31.1.1 Xshell软件安装
免费体验版的Xshell下载入口地址是
https://www.xshell.com/zh/free-for-home-school/
进入网站填写信息后Xshell官方会将免费下载链接下发到填写的邮箱 将邮箱中的链接复制粘贴到浏览器即可进行下载下载安装。
31.1.2 Xshell的使用
Xshell安装好之后双击运行软件在弹出的会话窗口点击“新建”添加串口连接 然后在“连接”项中将协议设置为“Serial”如下图所示 接着去“连接”中的“串口”处选择串口号下图的COM20只是一个例子按照下一节的说明使用USB串口连接W800后端口好可能不一样和设置通信参数如图所示 设置好之后点击右下角的“连接”即可连接指定串口设备了。
31.1.3 硬件连接
W800的硬件接口图如下 按上图将硬件连接好之后使用Xshell打开串口随后再将上图的WIFI_RESET引脚短接GND复位W800之后W800就会一直往Xshell发送字符’C’如下图所示 进入此状态后就可以按照下节内容烧写固件了。
31.1.4 固件烧写步骤
W800进入烧写状态后在Xshell的显示窗口点击鼠标右键选择“传输”-“YMODEM(Y)”-“用YMODEM发送(S)”如下图所示 随后进入资料包中固件所在位置选择w800.fls开始烧写 最后等待传输烧写完毕 烧写完成后W800会继续对外输出’C’表示还在烧写状态此时将BOOT引脚与GND断开并且再次手动复位W800就可以让W800进入正常工作状态如下图所示 当Xshell打印出“user task”后就表明W800已经进入工作状态了。此时可以在Xshell窗口直接输入指令“AT”并车键Xshell不会显示输入指令如果返回“OK”固件烧写成功了如下图所示 31.2 AT指令简介
W800的固件AT指令在官方手册《WM_W800_SDK_AT指令用户手册.pdf》中有详细描述这个文档已经放到本书的配套资料中。
W800支持的AT指令非常的多本书仅展现几个常用的指令。
指令指令格式响应说明AT“AT\r\n”“OK\r\n\r\n”测试指令响应OK表示测试成功ATZ“ATZ\r\n”“OK\r\n\r\n”软件复位响应OK表示复位指令发送成功复位后会响应“user task”ATE“ATE\r\n”“OK\r\n\r\n”回显切换指令输入一次切换一次状态在回显状态下W800会连带指令和结果一起响应ATRSTF“ATRSTF\r\n”“OK\r\n\r\n”恢复FLASH中的出厂设置。恢复后的设置需系统重启后才能生效ATWPRT“ATWPRT[!?][type]\r\n”例如ATWPRT0\r\n“OK[type]\r\n\r\n”例如OK0\r\n\r\n设置 /查询无线网络类型:l 0:STA;l 2:SoftAP;l 3:APSTAATWSCAN“ATWSCAN\r\n”“OK……”该指令仅在无线网络类型为 STA时有效用于扫描无线网络完成后返回。ATSSID“ATSSID[!?][SSID]”例如ATSSID100ask\r\n“OK[ssid]\r\n”设置 /查询无线网络名称即 SSIDATKEY“ATKEY[!?][format],[index],[key]\r\n”“OK[format,index,key]\r\n”设置 /查询网络密钥ATWJOIN“ATWJOIN\r\n”“OKltbssidgt,lttypegt…\r\n”如果当前网络类型为为 STA 时本指令功能为连接 AP。如果当前网络类型SoftAP或者APSTA 时本指令功能为创建ATWLEAV“ATWLEAV\r\n”“OK\r\n\r\n”无线网络类型为 STA时用于断开当前无线网络。ATNIP“ATNIP[!?][type]…\r\n”“OK[type]…\r\n”当 无线网卡作为STA时该指令用于设置 /查询本端 IP地址。ATLKSTT“ATLKSTT\r\n”OK[status,ip,netmask,gateway,dns1,dns2]\r\n\r\n查询本端网络连接状态ATSKCT“ATSKCT[protocol]…\r\n”“OKltsocketgt\r\n”建立 socket。在 client模式等待连接完成成功或失败后返回在 server模式下创建完成后直接返回。ATSKCLS“ATSKCLSltsocketgt\r\n”“OK\r\n\r\n”关闭指定的socket。ATSKSND“ATSKSND[socket],[size]\r\n”“OK\r\n\r\n”响应OK后发送数据流ATSKRCV“ATSKRCV[socket],[size]\r\n”“OK\r\n\r\n”响应OK后会将rxdata中的数据发送到串口
31.3 模块配置
本实验只使用到UART请参考前文的操作在FSP中配置UART及其引脚。本次实验仅展示配置结果。
31.3.1 硬件连接
板载W800的原理图如下图所示 使用到的RA6M5处理器引脚是P505和P506对应于SCI的UART6的TX/RX引脚。
31.3.2 UART模块配置
UART6 UART7 31.4 驱动程序
31.4.1 设备对象封装
在《30.3设备对象封装》的基础上对定时器的设备对象进行了改进添加了一个Timeout函数它实现了延时函数。代码如下:
static struct TimerDev gSystickDevice {.name Systick,.channel 0xFF,.status 0,.Init SystickInit,.Start NULL,.Stop NULL,.Read NULL,.Timeout HAL_Delay,.next NULL
};
void SystickTimerDevicesCreate(void)
{TimerDeviceInsert(gSystickDevice);gSystickDevice.Init(gSystickDevice);
}
static int HAL_Delay(struct TimerDev *ptdev, uint32_t timeout)
{if(NULL ptdev) return -EINVAL;if(0 ptdev-status) return -EIO;uint32_t dwStart dwTick;uint32_t dwWait timeout;/* Add a freq to guarantee minimum wait */if (dwWait HAL_MAX_DELAY){dwWait (uint32_t)(1);}while((dwTick - dwStart) dwWait){}return ESUCCESS;
}31.4.2 初始化UART
本次实验会用到两个UART
UART6和WiFi蓝牙芯片W800通信UART7调试打印
对于UART6由于W800每次收发的数据长度是不确定的不适合用DTC或者DMA来辅助传输数据因而使用环形缓冲区。在初始化UART6的时候申请了一个环形缓冲区而UART7作为调试串口没有使用环形缓冲区。代码如下
static struct RingBuffer *gWiFiBuffer NULL;
static struct UartDev gWiFiDevice {.name WiFi Uart,.channel 6,.Init UARTDrvInit,.Read UARTDrvRead,.Write UARTDrvWrite,.next NULL
};
void UartDevicesCreate(void)
{UartDeviceInsert(gLogDevice);UartDeviceInsert(gWiFiDevice);gLogDevice.Init(gLogDevice);
}
static int UARTDrvInit(struct UartDev *ptdev)
{if(NULL ptdev) return -EINVAL;switch(ptdev-channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{fsp_err_t err g_uart6.p_api-open(g_uart6.p_ctrl, g_uart6.p_cfg);assert(FSP_SUCCESS err);gWiFiBuffer RingBufferNew(1024);assert(NULL ! gWiFiBuffer);break;}case 7:{fsp_err_t err g_uart7.p_api-open(g_uart7.p_ctrl, g_uart7.p_cfg);assert(FSP_SUCCESS err);break;}case 8:case 9:break;default:break;}return ESUCCESS;
}31.4.3 中断回调函数
对于UART6和UART7需要提供中断回调函数。它们的操作是类似的UART6需要处理“发送完成”和“接收完成”两种情况而UART7只需要处理“发送完成”。UART6的中断回调函数代码如下
void uart6_callback(uart_callback_args_t * p_args)
{switch(p_args-event){case UART_EVENT_RX_COMPLETE:{break;}case UART_EVENT_TX_COMPLETE:{gUart6TxCplt true;break;}case UART_EVENT_RX_CHAR:{gWiFiBuffer-Write(gWiFiBuffer, (unsigned char*)p_args-data, 1);break;}case UART_EVENT_ERR_PARITY:case UART_EVENT_ERR_FRAMING:case UART_EVENT_ERR_OVERFLOW:case UART_EVENT_BREAK_DETECT:case UART_EVENT_TX_DATA_EMPTY:break;default:break; }
}第11行将UART6的发送完成标志写为true第16行如果接收到数据将它写入到UART6的缓冲区中
31.4.4 UART发送函数
UART设备的发送函数比较简单调用UART6和UART7设备结构体的write函数即可
static int UARTDrvWrite(struct UartDev *ptdev, unsigned char * const buf, unsigned int length)
{if(NULL ptdev) return -EINVAL;if(NULL buf) return -EINVAL;if(0 length) return -EINVAL;switch(ptdev-channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{fsp_err_t err g_uart6.p_api-write(g_uart6.p_ctrl, buf, length);assert(FSP_SUCCESS err);UART6WaitTxCplt();break;}case 7:{fsp_err_t err g_uart7.p_api-write(g_uart7.p_ctrl, buf, length);assert(FSP_SUCCESS err);UART7WaitTxCplt();break;}case 8:case 9:break;default:break;}return ESUCCESS;
}31.4.5 UART数据读取函数
对于UART6读取UART的数据时是从它的环形缓冲区里读取数据。对于UART7调用FSP封装的函数读数据。代码如下
static int UARTDrvRead(struct UartDev *ptdev, unsigned char *buf, unsigned int length)
{if(NULL ptdev) return -EINVAL;if(NULL buf) return -EINVAL;if(0 length) return -EINVAL;switch(ptdev-channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{if(gWiFiBuffer-Read(gWiFiBuffer, buf, length) ! length)return -EIO;break;}case 7:{fsp_err_t err g_uart7.p_api-read(g_uart7.p_ctrl, buf, length);assert(FSP_SUCCESS err);UART7WaitRxCplt();break;}case 8:case 9:break;default:break;}return (int)length;
}31.5 W800 WiFi蓝牙模块
WiFi蓝牙模块的设备驱动代码在Devices/wifi_bluetooth文件夹中文件dev_wifi_bt.c/.h。
31.5.1 初始化W800
要初始化W800实际是初始化W800用到的串口设备即初始化UART6
static UartDevice *pWiFiBtDev NULL;
int WiFiBtDevInit(void)
{pWiFiBtDev UartDeviceFind(WiFi);if(NULLpWiFiBtDev) return -ENXIO;if(pWiFiBtDev-Init(pWiFiBtDev) ! ESUCCESS) return -EIO;return ESUCCESS;
}31.5.2 AT指令返回判定
在AT指令简介中已经直到每个AT指令都有其对应的响应。对于大多数指令只需要判断它收到的回应是不是“OK”即可。将这个功能封装为如下函数
static int WiFiBtDevCmdRet(const char *ret)
{unsigned short timeout 3000;unsigned char i 0;unsigned char buf[128] {0};while(timeout){if(strstr((char*)buf, ret)){return ESUCCESS;}else if(strstr((char*)buf, ERR)){return -EIO;}else if(pWiFiBtDev-Read(pWiFiBtDev, buf[i], 1)1){printf(%c, buf[i]);i;}mdelay(1);timeout--;}return -EIO;
}第08行如果接收的数据中读出来有指定响应字符串就返回ESUCCESS第12行如果响应中有“ERR”就表示指令失败返回-EIO第16行如果想要的响应或者错误响应都没有则继续从缓冲区中读取数据并打印出来第21~22行超时等待倒计时如果是超时退出则返回-EIO
31.5.3 设置W800的工作模式
W800的WiFi功能有3个工作模式STA、SoftAP和APSTA定义了一个枚举类型
typedef enum{STA 0,SoftAP 2,APSTA 3
}WorkType;发出指令“ATWPRTlttypegt\r\n”注意必须要有’\r’就可以设置W800的工作模式。具体的实现很简单调用UART设备的write函数发送这个字符串然后等待响应即可。代码如下
int WiFiBtDevSetWorkType(WorkType type)
{char str[64];sprintf(str, ATWPRT%d\r\n, type);if(pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)str, strlen(str)) ! ESUCCESS)return -EIO;mdelay(100);return WiFiBtDevCmdRet(OK);
}31.5.4 设置W800的DHCP状态
如果想让W800在连接上了热点之后自动获得IP地址则需要使能W800的DHCP功能如果想要固定W800的IP地址则需要关闭DHCP自动分配功能并且指定IP。
使能DHCP功能的指令是“ATNIP0\r\n”代码如下
int WiFiBtDevEnableDHCP(void)
{int ret -EIO;char *str ATNIP0\r\n;ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)str, strlen(str));if(ESUCCESS! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);return ret;
}要手工设置IP使用指令“ATNIP1,[IP],[net_mask],[gate_way]\r\n”它会关闭DHCP功能、指定IP、子网掩码和默认网关。代码如下
int WiFiBtDevDisableDHCP(const char *ip, const char *netmask, const char *gateway)
{int ret -EIO;char str[64];sprintf(str, ATNIP1,%s,%s,%s\r\n, ip, netmask, gateway);ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)str, strlen(str));if(ESUCCESS! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);return ret;
}31.5.5 连接指定热点
连接热点需要3个指令
设置热点的名称ATSSID[SSID]\r\n设置连接热点的密码ATKEY[密钥格式],[密钥索引号],[密钥字符串]\r\n连接热点ATWJOIN\r\n
对于设置密钥的参数手册中是这样解释的
密钥格式0-Hex格式1-ASCII格式密钥索引号1~4用于WEP加密密钥其它加密方式固定为0密钥字符串以双引号包围根据不同的安全模式密钥使用的长度与格式要求定义如下 本实验将连接热点的三个指令封装到一个函数中调用者只需要传入连接热点的名称和密码即可
int WiFiBtDevConnectWiFi(const char *name, const char *password)
{int ret -EIO;char ssid[32];sprintf(ssid, ATSSID%s\r\n, name);ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)ssid, strlen(ssid));if(ESUCCESS ! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);if(ESUCCESS ! ret) return ret;char key[32] ;sprintf(key, ATKEY1,0,%s\r\n, password);ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)key, strlen(key));if(ESUCCESS ! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);if(ESUCCESS ! ret) return ret;char join[32] ATWJOIN\r\n;ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)join, strlen(join));if(ESUCCESS ! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);int ret1 WiFiBtDevCmdRet(\r\n\r\n);if(retESUCCESS ret1)mdelay(1000);return ret;
}31.5.6 断开热点连接
断开热点的连接只需要发送指令“ATWLEAV\r\n”即可代码如下
int WiFiBtDevDisconnectWiFi(void)
{int ret -EIO;char *leavw ATWLEAV\r\n;ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)leavw, strlen(leavw));
if(ESUCCESS ! ret) return ret;
mdelay(100);
ret WiFiBtDevCmdRet(“OK”);
return ret;
}31.5.7 获取本地IP
当连接了热点并且使能了DHCP自动获取IP功能用户可能想要知道本机分配的IP地址是多少这时候就需要使用指令“ATLKSTT\r\n”来查询本机IP了代码如下
int WiFiBtDevGetLocalIP(void)
{int ret -EIO;char lkstt[32] ATLKSTT\r\n;ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)lkstt, strlen(lkstt));if(ESUCCESS ! ret) return ret;mdelay(100);unsigned short timeout 100;while(timeout){unsigned char c 0;if(pWiFiBtDev-Read(pWiFiBtDev, c, 1)1){printf(%c, c);}mdelay(1);timeout--;}return ESUCCESS;
}由于此指令需要打印”OK”之后的类容不容易判定结尾因而故意设置很长的超时以便接收到全部数据再把它打印出来。读者可以改进代码从中解析出IP。
31.5.8 建立socket连接
W800连上热点之后要和某个服务器进行网络通信需要使用指令“ATSKCT”。这个指令需要传入的参数比较多协议、本机角色、IP地址、远端端口和本机端口本书对此指令的参数进行了封装用一个结构体描述连接信息
typedef enum{TCP 0,UDP 1
}NetworkProtocol;typedef enum{Client 0,Server 1
}LocalRole;typedef struct{NetworkProtocol Protocl;LocalRole Role;char *IP;unsigned int RemotePort;unsigned int LocalPort;unsigned int SocketPort;
}ConnectInfo;应用程序要构造一个ConnectInfo结构体然后构造AT指令发送给W800。这就是网络连接函数如果连接成功还要记录端口号。代码如下
int WiFiBtDevConnect(ConnectInfo *info)
{int ret -EIO;char skct[128];sprintf(skct, ATSKCT%d,%d,%s,%d,%d\r\n, \info-Protocl, \info-Role, \info-IP, \info-RemotePort, \info-LocalPort);ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)skct, strlen(skct));if(ESUCCESS ! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);if(ESUCCESS ! ret) return ret;/* 获取连接成功后的socket号 */unsigned short timeout 1000;unsigned char i 0;unsigned char buf[32] {0};while(timeout){if(strstr((char*)buf, \r\n)){printf(\r\n);break;}else if(pWiFiBtDev-Read(pWiFiBtDev, buf[i], 1)1){printf(%c, buf[i]);i;}mdelay(1);timeout--;}for(i0;buf[i]!\r; i){if(buf[i]0 buf[i]9){buf[i] buf[i] - 0;info-SocketPort info-SocketPort*10 buf[i];}}printf(IP:%s - SocketPort:%d\r\n, info-IP, info-SocketPort);return ret;
}第37行返回的socket号最后一个数字紧跟的是’\r’在解析socket号的时候不需要这个值因而for循环的判定就是直到读取到’\r’为止就不进行ASCII到数值的转换计算。
31.5.9 断开指定socket的连接
断开网络连接使用的指令是”ATSKCLSltsocketgt\r\n”本书封装了如下函数
int WiFiBtDevDisconnect(ConnectInfo info)
{int ret -EIO;char skcls[32];sprintf(skcls, ATSKCLS%d\r\n, info.SocketPort);ret pWiFiBtDev-Write(pWiFiBtDev, (unsigned char*)skcls, strlen(skcls));if(ESUCCESS ! ret) return ret;mdelay(100);ret WiFiBtDevCmdRet(OK);return ret;
}31.6 测试程序
本实验中先使用网络调试助手开启了Windows电脑的TCP服务然后启动板子。板子上的程序先使用W800连接热点然后后Windows电脑建立TCP连接。 测试函数代码如下所示
void WiFiBtAppTest(void)
{UartDevicesRegister();TimerDevicesRegister();WiFiBtDevInit();WiFiBtDevSetWorkType(STA);WiFiBtDevEnableDHCP();WiFiBtDevConnectWiFi(X-IOT, x-iot.cq);WiFiBtDevGetLocalIP();ConnectInfo connect {.Protocl TCP,.Role Client,.IP 192.168.50.193,.RemotePort 8080,.LocalPort 1024};int ret WiFiBtDevConnect(connect);while(1){if(ret ! ESUCCESS){ret WiFiBtDevConnect(connect);delay(1);}}
}31.7 测试结果
打开串口助手观察信息打开网络助手启动TCP服务。然后将编译出来的二进制文件烧写到板子上运行就能在串口助手上观察到W800的响应信息在网络助手上观察到网络连接信息如下图所示 本章完