一. 简介
本次将使用正点原子的ESP8266 WIFI模块,来实现PC与FPGA之间的TCP通讯,其中ESP8266与FPGA之间的接口是UART。
二. 正点原子的ESP8266 WIFI模块介绍
模块实物图如下,到手就可以使用了,RST和IO_0两个IO口不接或者接高电平就可以了。
在使用之前,需要通过AT指令对模块进行配置,比如说是AP模式,还是STA模式。AP模式就是模块作为无线 WIFI 热点,允许其他 WIFI 设备连接到本模块;STA模式就是连接到其它的WIFI设备。具体的指令可以在正点原子论坛上下载。
三. ESP8266初始化
本次是将ESP8266配置成AP模式,充当TCP服务器,配置的AT指令如下(每个指令末尾均带有换行)。FPGA端只需要通过串口将这些AT指令依次发送出去即可完成配置。
先将这些指令的数据存储到memory中,就像下面这样。
reg[7:0] rom0[14:0];
reg[7:0] rom1[10:0];
reg[7:0] rom2[43:0];
reg[7:0] rom3[15:0];
reg[7:0] rom4[44:0];
always@(*)begin
rom0[0] = "A";
rom0[1] = "T";
rom0[2] = "+";
rom0[3] = "C";
rom0[4] = "W";
rom0[5] = "M";
rom0[6] = "O";
rom0[7] = "D";
rom0[8] = "E";
rom0[9] = "=";
rom0[10] = "2";
rom0[11] = 8'h0D;
rom0[12] = 8'h0A;
end
然后再初始化模块中依次发送出去即可,需要注意的是,每个指令的发送需要间隔一定的时间,因为ESP8266每接收到一个指令会返回一个应答(通常是OK),不等待的话,可能会导致下条指令接收错误。
全部发送完成后,模块会跳到S7状态,向外部发送初始化完成信号。
localparam S0 = 'd0; //IDLE
localparam S1 = 'd1; //AT + CWMODE = 2
localparam S2 = 'd2; //AT + RST
localparam S3 = 'd3; //AT + CWSP="ATK-ESP8266","12345678",1,4
localparam S4 = 'd4; //AT + CIPMUX = 1
localparam S5 = 'd5; //AT+CIPSERVER=1,8086
localparam S6 = 'd6; //wait 10ms
localparam S7 = 'd7; //ack
四. 发送数据
由于ESP8266配置的模式不支持透传模式,所以每次发送数据的时候,需要发送一个发送的AT指令,如下。这条AT指令发送完成后,也不能立马发送数据,需要等ESP8266做出响应后才能。这里发送数据的时候,不需要带换行。ID0: 第一个连接到tcp服务器的客户端。
五. 数据接收
当ESP8266模块,接收到数据的时候,会做出如下的响应。其中4是第四个连接到tcp服务器的客户端。
在接收数据的时候,需要先解析前面的+IPD,4,n: 这些字符。解析正确后,就将后面接收到的n字节数据作为有效数据。
//检测头部序列 是否正确
always@(posedge clk or negedge rst_n) begin
if( rst_n == 1'b0 )
head_index <= 'd0;
else if( state == S_HEAD )
if( head_index == 'd0 && esp8266_rx_done_i == 1'b1 )
if( esp8266_rx_data_i == "+" )
head_index <= head_index + 1'b1;
else
head_index <= 'd0;
else if( head_index == 'd1 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "I" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd2 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "P" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd3 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "D" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd4 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "," )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd5 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "0" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd6 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "," )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd7 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "1" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd8 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == "6" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else if( head_index == 'd9 && esp8266_rx_done_i == 1'b1)
if( esp8266_rx_data_i == ":" )
head_index <= head_index + 1'b1;
else if( esp8266_rx_data_i == "+" )
head_index <= 'd1;
else
head_index <= 'd0;
else
head_index <= head_index;
else
head_index <= 'd0;
end
六. 整体设计
本次的设计主要是为了完成如下数据包格式的收发,所以设计不仅仅是单纯的收发,还包括数据包的整合和解析。有相同需求的,可以参考参考。
整个设计模块的层次图如下(高云的EDA软件RTL图有点拉胯)
关注微信公众号 FPGA之旅 回复 FPGA_ESP8266 获取完整工程。