差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
wifi_esp8266通信系统设计 [2018/11/01 13:29]
anran [实验任务]
wifi_esp8266通信系统设计 [2019/04/22 13:59] (当前版本)
gongyu
行 1: 行 1:
 =====WIFI_ESP8266通信系统设计===== =====WIFI_ESP8266通信系统设计=====
 +
 ----- -----
 ====实验任务==== ====实验任务====
行 9: 行 10:
 ====实验目的==== ====实验目的====
  
 +在前面串口监视系统设计实验中我们学习了UART总线的驱动原理及设计实现,本实验主要了解WIFI通信TCP协议,熟悉AT指令集,掌握ESP8266模块的配置方法,最终通过FPGA编程实现对ESP8266模块的配置应用。
 +  * 熟悉基本网络通信原理,简要了解TCP协议
 +  * 熟悉AT指令集,掌握ESP8266模块的配置方法
 +  * FPGA通过UART配置ESP8266模块的设计实现
 +  * 完成WIFI_ESP8266通信系统设计实现
  
 ====设计框图==== ====设计框图====
  
 +根据前面的实验解析我们可以得知,该设计可以拆分成以下功能模块实现,
 +  * WIFI_ESP8266:TOP模块,同时负责对ESP8266配置和处理接收的数据。
 +  * Baud:控制UART通信数据传输速率。
 +  * Uart_Rx:根据数据传输速率节拍控制UART数据接收。
 +  * Uart_Tx:根据数据传输速率节拍控制UART数据发送。
 +  * Segment_scan:通过驱动底板扫描式数码管将串口接收的数据显示出来。
  
 +{{:​14-Top-Down层次设计.png?​500|Top-Down层次设计}} {{:​14-模块结构设计.png?​500|模块结构设计}}
 ====实验原理==== ====实验原理====
 +
 ===ESP8266模块介绍=== ===ESP8266模块介绍===
 +
 +ESP8266是ai-thinker公司推出的一款无线WIFI模块,可以通过配置,和单片机上的串口进行通信,利用WIFI传输数据。模块内部使用乐鑫推出的低功耗高集成度的WIFI芯片,ESP8266EX内置超低功耗32位RISK处理器,CPU最高时钟频率可达160Mhz,支持实时操作系统RTOS,和WIFI协议栈,可将高达80%的处理能力留给编程与开发。
 +
 ===ESP8266模块连接=== ===ESP8266模块连接===
 +
 +STEP BaseBoard V3.0底板上的WIFI通信模块ESP8266-12F电路图如下:
 +
 +{{:​14-ESP8266-12F电路连接.png?​800|ESP8266-12F电路连接}} ​
 +
 ===ESP8266模块配置流程=== ===ESP8266模块配置流程===
 +
 +(1)取下小脚丫底板,将Baseboard的GPIO29与GPIO26用杜邦线连接起来,将GPIO27与GPIO28连接起来,这样就实现了CP2102与ESP8266的互联。
 +
 +{{:​14-ESP配置1.png?​600|CP2102与ESP8266手工互连}} ​
 +
 +(2)打开串口调试助手,发送“AT”(AT指令集后要换行),发送,如果连接无误效果如下:
 +
 +{{:​14-ESP配置2.png?​600|ESP8266串口回路测试}} ​
 +
 +(3)保险起见,我们复位一下模块,发送AT+RST,如无误如下图所示(乱码为正常现象,有返回ready即可):
 +
 +{{:​14-ESP配置3.png?​600|ESP8266软件复位}} ​
 +
 +(4)如果你在一个存在WIFI的环境下,可以将ESP8266连入路由器,并获得IP,首先,配置ESP8266的工作模式为sta,输入AT+CWMODE=1,如无误如下图所示:
 +
 +{{:​14-ESP配置4.png?​600|ESP8266配置STA模式}} ​
 +
 +然后,我们扫描附近WIFI:
 +
 +{{:​14-ESP配置5.png?​600|显示无线列表}} ​
 +
 +找到我们要连入的WIFI,本例中,我们连入“FHQ”,密码为123456789
 +
 +{{:​14-ESP配置6.png?​600|配置无线连接}} ​
 +
 +我们可以从图片中看到已经成功连入并获取到IP,你可以使用AT+CWQAP来断开WIFI。
 +
 +(5)成功连入WIFI之后,我们就要开始配置透传了,首先,配置连接模式为单连接:
 +
 +{{:​14-ESP配置7.png?​600|配置单连接模式}} ​
 +
 +(6)打开网络调试助手,获取本机IP与端口:
 +
 +{{:​14-ESP配置8.png?​600|网络调试助手}} ​
 +
 +我们将协议类型改为TCP Server,端口号改为1234。
 +
 +{{:​14-ESP配置9.png?​600|配置网络调试助手}} ​
 +
 +(7)回到串口调试助手,发送AT+CIPSTART="​TCP","​192.168.20.125",​1234,连入该端口。发送AT+CIPMODE=1打开透传。
 +
 +{{:​14-ESP配置10.png?​600|通过串口调试助手配置ESP8266连接电脑的服务器,打开透传功能}} ​
 +
 +发送AT+CIPSEND进入透传模式:
 +
 +{{:​14-ESP配置11.png?​600|进入透传功能}} ​
 +
 +如果要退出透传,则发送不带回车的“+++”即可退出透传模式。
 +
 +(8)我们在成功进入透传模式后,在串口助手中发送”hello”,如连接无误,你可以在网络调试助手端接收到“hello”。 ​
 +
 +{{:​14-ESP配置12.png?​800|透传通信}} ​
 +
 +你也可以在网络调试助手端发送数据,串口段也可接收到:
 +
 +{{:​14-ESP配置13.png?​800|透传通信}} ​
 +
 +这样就完成了ESP8266的网络通讯。
 +
 ===系统总体实现=== ===系统总体实现===
  
 +本实验我们将ESP8266配置成SoftAP模式,同时配置成服务器,采用下表中的指令对ESP8266模块进行配置。
 +
 +ESP8266配置指令表:
 +
 +|序号 |发送指令 |作用|
 +|1 |AT+CWMODE=2 |设置模块WIFI模式为SoftAP模式|
 +|2 |AT+CWSAP=”STEP_FPGA","​12345678",​5,​4 |设置模块的AP参数:SSID为STEP_FPGA,密码为12345678,通道号为5,加密方式为WPA_WPA2_PSK|
 +|3 |AT+RST |重启生效|
 +|4 |AT+CIPMUX=1 |开启多连接|
 +|5 |AT+CIPSERVER=1,​8686 |开启SERVER模式,端口设置为8686|
 +
 +这里我们发送的各种指令,实际发送的数据为字符对应的ASCII码,所以在FPGA程序实现的时候就是要取AT指令的ASCII码值,例如”AT+RST”复位指令,通过串口调试助手发送的数据为<​0x41,​0x54,​0x2B,​0x52,​0x53,​0x54,​0x0D,​0x0A>​,每个字符的ASCII码都是8位位宽的数据,其中0x41为A的ASCII码,0x0D和0x0A为回车换行的ASCII码, Verilog语言中使用双引号获取字符的ASCII码。
 +
 +变量char表示AT指令数据,变量num表示AT指令中包含的字符数量(包含回车和换行),程序实现如下:
 +
 +<code verilog>
 +MAIN:begin
 +        if(cnt_main >= 4'd5) cnt_main <= 4'​d5; ​   //write mode
 +        else cnt_main <= cnt_main + 1'b1;
 +        case(cnt_main)
 +            4'd0: begin num<​=8'​d13;​ char<​={"​AT+CWMODE=2",​16'​h0d0a};​state<​=TXMD;​ end
 +            4'd1: begin num<​=8'​d37;​ char<​={"​AT+CWSAP=",​8'​h22,"​STEP_FPGA",​8'​h22,",",​8'​h22,"​12345678",​8'​h22,",​5,​4",​16'​h0d0a};​state <= TXMD; end
 +            4'd2: begin num<​=8'​d08;​ char <= {"​AT+RST",​16'​h0d0a};​state <= TXMD; end
 +            4'd3: begin num<​=8'​d13;​ char<​={"​AT+CIPMUX=1",​16'​h0d0a};​state<​=TXMD;​ end
 +            4'd4: begin num <= 8'd21; char <= {"​AT+CIPSERVER=1,​8686",​16'​h0d0a};​state <= TXMD; end
 +            4'd5: begin state <= REMD; end
 +            default: state <= IDLE;
 +        endcase
 +    end
 +</​code>​
 +
 +我们使用状态机的MAIN状态控制我们需要配置的所有指令数据,你可以比喻成帝王,把握整个设计的大局。
 +
 +使用AT指令集控制ESP8266模块是UART接口,我们前面串口监视系统设计实验详细讲解了UART通信,本实验需要例化UART模块进行数据传输,如下:
 +
 +<code verilog>
 +/////////////////////////​uart_tx module//////////////////////​
 +Baud #
 +(
 +.BPS_PARA ​              ​(BPS_PARA ​      )
 +)  ​
 +Baud_tx
 +(
 +.clk                    (clk            ),  //​系统时钟 12MHz
 +.rst_n ​                 (rst_n ​         ),  //​系统复位,低有效
 +.bps_en ​                ​(bps_en_tx ​     ),  //​接收时钟使能
 +.bps_clk ​               (bps_clk_tx ​    ​) ​  //​接收时钟输出
 +);
 +
 +Uart_Tx Uart_Tx_uut
 +(
 +.clk                    (clk            ),  //​系统时钟 12MHz
 +.rst_n ​                 (rst_n ​         ),  //​系统复位,低有效
 +.bps_en ​                ​(bps_en_tx ​     ),  //​发送时钟使能
 +.bps_clk ​               (bps_clk_tx ​    ​), ​ //​发送时钟输入
 +.tx_data_valid ​         (tx_data_valid ​ ),  //​发送数据有效脉冲
 +.tx_data_in ​            ​(tx_data_in ​    ​), ​ //​要发送的数据
 +.uart_tx ​               (wifi_tx ​       )   //​UART发送输出
 +);
 +</​code>​
 +
 +Baud模块和Uart_Tx模块配合完成UART发送数据的功能,前级电路通过tx_data_valid和tx_data_in[7:​0]端口将数据传递给Uart_Tx模块,然后Uart_Tx模块将数据按照UART总线时序发送出去,框图如下:
 +
 +{{:​6-UART发送功能设计实现.png?​600|UART发送功能设计实现}}
 +
 +我们使用之前设计的UART发送模块将需要传递的数据通过UART总线发送出去,你可以比喻成士兵,是具体的执行人员。
 +
 +帝王把握整体设计,有哪些数据需要传输;士兵只会干活,UART传输实现,每次传输8位数据;我们还需要一名将军,按照帝王的要求指挥士兵完成任务。所以每当MAIN(帝王)状态跳转到TXMD(将军)状态后,TXMD状态完成对Uart_Tx模块tx_data_valid和tx_data_in[7:​0]端口的配置。
 +
 +<code verilog>
 +TXMD:begin
 +        case(cnt_txmd)
 +            3'​d0: ​  ​if(bps_en_tx) cnt_txmd <= cnt_txmd; ​
 +                    else cnt_txmd <= cnt_txmd + 1'b1;
 +            3'​d1: ​  begin num <= num - 1'b1; cnt_txmd <= cnt_txmd + 1'b1; end
 +            3'​d2: ​  begin tx_data_valid <= 1'b1; tx_data_in <= char[(num*8)+:​8];​ cnt_txmd <= cnt_txmd + 1'b1; end
 +            3'​d3: ​  ​begin ​
 +                        tx_data_valid <= 1'​b0; ​
 +                        if(num>​=1'​b1) cnt_txmd <= 3'd0;
 +                        else cnt_txmd <= cnt_txmd + 1'b1;
 +                    end
 +            3'​d4: ​  begin state <= DELAY; cnt_txmd <= 1'b0; end
 +            default: state <= IDLE;
 +        endcase
 +    end
 +</​code>​
 +
 +到这里对ESP8266的配置已经完成了,假设用手机或电脑连接该网络:STEP_FPGA,同时打开网络调试助手作为TCP Client连接TCP服务器:192.168.4.1,端口号:8686,那么就可以给ESP8266发数据了,ESP8266模块接收到WIFI数据,然后以UART总线时序发送给FPGA,FPGA需要UART总线的接收模块接收数据,所以设计中还需要对UART接收功能模块的例化,程序实现如下:
 +
 +<code verilog>
 +////////////////////////​uart_rx module/////////////////////​
 +Baud #
 +(
 +.BPS_PARA ​              ​(BPS_PARA ​      )
 +) Baud_rx
 +(   
 +.clk                    (clk            ),  //​系统时钟 12MHz
 +.rst_n ​                 (rst_n ​         ),  //​系统复位,低有效
 +.bps_en ​                ​(bps_en_rx ​     ),  //​接收时钟使能
 +.bps_clk ​               (bps_clk_rx ​    ​) ​  //​接收时钟输出
 +);
 +
 +Uart_Rx Uart_Rx_uut
 +(
 +.clk                    (clk            ),  //​系统时钟 12MHz
 +.rst_n ​                 (rst_n ​         ),  //​系统复位,低有效
 +.bps_en ​                ​(bps_en_rx ​     ),  //​接收时钟使能
 +.bps_clk ​               (bps_clk_rx ​    ​), ​ //​接收时钟输入
 +.uart_rx ​               (wifi_rx ​       ),  //​UART接收输入
 +.rx_data_valid ​         (rx_data_valid ​ ),  //​接收数据有效脉冲
 +.rx_data_out ​           (rx_data_out ​   )   //​接收到的数据
 +);
 +</​code>​
 +
 +Baud模块和Uart_Rx模块配合完成UART接收数据的功能,Uart_Rx模块按照UART总线时序接收数据,然后将接收到的数据通过rx_data_valid和rx_data_out[7:​0]端口输出给后级电路,框图如下:
 +
 +{{:​6-UART接收功能设计实现.png?​600|UART接收功能设计实现}}
 +
 +{{:​14-WIFI通信.png?​800|WIFI通信}}
 +
 +当我们连接服务器,使用网络调试助手发送数据<​123>​,ESP8266模块接收WIFI信号,并通过UART返回数据<​+IPD,​0,​3:​123>​,如上图所示,想要将123显示在数码管上,需要对UART接收的数据进行解析,包括两个方面,
 +1)接收到的数据中<​+IPD,​0,​3:>​部分不能显示,需要排除,只显示数据<​123>​
 +2)数据以ASCII码形式接收,需要解析成字符数据
 +
 +UART数据中被舍弃的数据<​+IPD,​0,​3:>​,我们可以简单的使用加号<​+>​和冒号<:>​来控制显示的部分,例如显示冒号以后且加号以前的数据,程序实现如下
 +
 +<code verilog>
 +//​解析UART通信,控制只显示有效数据部分
 +always @ (posedge clk or negedge rst_n) begin
 +    if(!rst_n) display_en <= 1'b0;
 +    else if((state == REMD)&&​(rx_data_valid))
 +        case(rx_data_out)
 +            ":":​ display_en <= 1'b1;
 +            "​+":​ display_en <= 1'b0;
 +            default: display_en <= display_en;
 +        endcase
 +    else display_en <= display_en;
 +end
 +</​code>​
 +
 +ASCII码数据译码成对应的字符数据,程序实现如下:
 +
 +<code verilog>
 +//​对接收的ASCII码值解码,只对应0~9的数字
 +always @ (posedge clk or negedge rst_n) begin
 +    if(!rst_n) seg_data_r <= 4'ha;
 +    else if((state == REMD)&&​(rx_data_valid)) ​
 +        case(rx_data_out)
 +            "​0":​ seg_data_r <= 4'd0;
 +            "​1":​ seg_data_r <= 4'd1;
 +            "​2":​ seg_data_r <= 4'd2;
 +            "​3":​ seg_data_r <= 4'd3;
 +            "​4":​ seg_data_r <= 4'd4;
 +            "​5":​ seg_data_r <= 4'd5;
 +            "​6":​ seg_data_r <= 4'd6;
 +            "​7":​ seg_data_r <= 4'd7;
 +            "​8":​ seg_data_r <= 4'd8;
 +            "​9":​ seg_data_r <= 4'd9;
 +            default: seg_data_r <= seg_data_r;
 +        endcase
 +    else seg_data_r <= seg_data_r;
 +end
 +</​code>​
 +
 +最后将显示部分的字符数据放到移位寄存器中缓存,程序实现如下:
 +
 +<code verilog>
 +reg             ​[35:​0] ​ seg_data;
 +//​移位寄存器,UART接收数据的buffer
 +always @ (posedge clk or negedge rst_n) begin
 +    if(!rst_n) begin
 +        seg_data <= 36'​haaaa_aaaa_a; ​   // 本实验a对应数码管字库为不显示
 +    end else if(!display_en_r2) begin   //
 +        seg_data <= 36'​haaaa_aaaa_a;​
 +    end else if(rx_data_valid_r1) begin
 +        seg_data <= {seg_data[31:​0],​seg_data_r};​
 +    end else seg_data <= seg_data;
 +end
 +</​code>​
 +
 +例化扫描式数码管驱动模块,将移位寄存器缓存的数据显示在数码管上,程序实现如下:
 +
 +<code verilog>
 +Segment_scan Segment_scan_uut
 +(
 +.clk                    (clk                ),      //​系统时钟 12MHz
 +.rst_n ​                 (rst_n ​             ),      //​系统复位 低有效
 +.dat_1 ​                 (seg_data[31:​28] ​   ),     //​SEG1 显示的数据输入
 +.dat_2 ​                 (seg_data[27:​24] ​   ),     //​SEG2 显示的数据输入
 +.dat_3 ​                 (seg_data[23:​20] ​   ),     //​SEG3 显示的数据输入
 +.dat_4 ​                 (seg_data[19:​16] ​   ),     //​SEG4 显示的数据输入
 +.dat_5 ​                 (seg_data[15:​12] ​   ),     //​SEG5 显示的数据输入
 +.dat_6 ​                 (seg_data[11:​ 8]    ),     //​SEG6 显示的数据输入
 +.dat_7 ​                 (seg_data[ 7: 4]    ),     //​SEG7 显示的数据输入
 +.dat_8 ​                 (seg_data[ 3: 0]    ),     //​SEG8 显示的数据输入
 +.dat_en ​                ​(8'​b1111_1111 ​      ​), ​    //​数码管数据位显示使能
 +.dot_en ​                ​(8'​b0000_0000 ​      ​), ​    //​数码管小数点位显示使能
 +.seg_rck ​               (seg_rck ​           ),      //​74HC595的RCK管脚
 +.seg_sck ​               (seg_sck ​           ),      //​74HC595的SCK管脚
 +.seg_din ​               (seg_din ​           )       //​74HC595的SER管脚
 +);
 +</​code>​
  
 ====实验步骤==== ====实验步骤====
行 32: 行 315:
  
 ====实验现象==== ====实验现象====
 +
 +将设计加载到FPGA,手机或电脑WIFI连接到STEP_FPGA网络上,打开网络调试助手配置成TCP Client连接TCP服务器:192.168.4.1,端口号:8686,发送0~9的阿拉伯数字,底板数码管就能显示出来,当一次发送超过8位数据,只显示后面的8位数据。例如,网络调试助手发送数据<​123>​,数码管显示123。
 +
 +{{:​14-实验现象.png?​600|实验现象}}