首先说说自己写这篇博客的目的,由于本人是做FPGA的,在实际项目过程中,需要用到wifi模块来传输fpga内的数据,所以考虑在自己的fpga板子(zedboard)上加上一个WiFi模块,查阅了相关资料,找到了这款口碑还不的wifi模块ESP8266。
ESP8266 WIFI芯片作为一款价格低廉,功能完备的wifi通信模块,用户利用它可以快速的搭建自己wifi热点,实现数据传输。该模块在某宝上有售,有兴趣的朋友可以买来开发调试。
ESP8266 实物图
要想正确的配置首先就得将该wifi模块与zedboard正确连接。下图是ESP8266的硬件接口示意图和功能列表。
模块管脚排列图
PIN | Function | Description |
1 | URXD |
|
2 | UTXD |
|
5 | RESET(GPIO 16) | 外部Reset信号,低电平复位,高电平工作(默认高); |
6 | GND | GND |
8 | VCC | 3.3V,模块供电; |
9 | ANT | WiFi Antenna |
11 | GPIO0 |
悬空:Flash Boot,工作模式; 下拉:UART Download,下载模式; |
12 | ADC | ADC,输入范围:0V-1V; |
13 | GPIO15 | 下拉:工作模式; |
14 | CH_PD |
|
15 | GPIO2 |
|
pin脚定义
主要需要连接的是URXD,UTXD,CH_PD,VCC,GND这几个接口。CH_PD和VCC一起连接到zedboard上3.3v的电压端口,GND接到zedboard上的GND端口,URXD和UTXD分别连接到zedboard上任选的通用GPIO口,无特定要求。这些连接可以通过杜邦线完成。
当完成硬件连接后,给zedboard上电,可以看到WiFi模块电源指示灯亮,这是用手机或电脑搜寻附近的WiFi信号,可以搜到该WiFi模块发出的开放式的WiFi热点。
接下来进行WiFi模块的配置工作。
关于该WiFi模块的配置,官方给了两种方式,第一种是用AT指令去配置,第二种是用他们公司内嵌的AI-CLOUD 的8266 SDK 来配置。作者使用的是第一种方式,对第二种方式不太了解,有兴趣的朋友可以看下这个链接,里面详细介绍了第二种配置方式。
关于AT指令的配置方式,官方给的方式是,将WiFi通过TTL转USB接口连接到电脑上的usb接口,并通过电脑上的串口调试助手,给WiFi模块发送AT配置指令。关于这种方式,已经有很多前辈做了相应的实验教程,可以看ESP8266 AT 指令这个链接学习。关于ESP8266全面的AT指令集,可以去它官网链接下载相关资料了解。
在看本人下面写的fpga配置WiFi模块教程之前,希望大家已经跟着上面前辈的教程至少配置过一遍。
其实用FPGA配置WiFi是基于用AT指令配置WiFi演变而来的,如果仔细做了之前的教程,应该能够理解,所谓使用AT指令配置WiFi,其实就是用电脑上的串口调试助手向WiFi模块发送AT指令,从而完成配置。现在需要用FPGA配置WiFi模块,即需要通过FPGA以串口通信的方式给WiFi模块发送配置指令。所以设计的关键就是要在FPGA内实现串口通信协议,同时要将自己需要的AT指令写在FPGA内的寄存器里。这里有几个注意点:
①AT指令需要转成(八位十六进制)ASCII码,每一个字符被转成一个八位十六进制的ASCII码,可以用转码链接实现转码
②每条AT指令末尾需要加上回车键的ASCII码,不然wifi模块无法正确识别配置指令这一点很容易被忽视,另外在windows操作系统下,回车键的ASCII码是0d和0a,注意用上面的转码链接将回车转成的ascII码只是0a,所以这里需要手动修改。
③AT指令被存在自己定义在FPGA内部的寄存器组中。以一个个字符按指令的顺序存储。
④关于串口通信(UART)我之后会给出我写的verilog代码,还不太懂串口通信原理的朋友也可以点击串口通信原理链接查看学习。另外这里要提醒一下,串口通信会设置波特率大小,ESP8266对配置它的串口波特率有硬性要求,必须为115200。这在乐鑫原厂硬件资料里可以查到。
在我的设计中,ESP8266工作模式被设置为TCP server 透传模式,IP地址为 192.168.4.2 侦听端口为 8084
由于这是一个项目中的小工程,我需要发送的数据是4094个fft数据,每个是八位16进制的形式,大家可以随机生成4094个模拟数据发送,如果数据足够多,满速WiFi传输速度大概为11.2kb/s,速率相对较慢,由于串口速率的限制影响了它本身的传输速率。
接下来我给出本设计全部的verliog源码,开发环境是vivado 2017.2,使用的FPGA板是xilinx公司的zedboard。
测试方法:将WIFI模块的发送端通过TTL转USB接口连到电脑上,然后zedboard上电,WiFi开启,电脑连接上WiFi信号,ip地址必须为192.168.4.2。若WiFi模块配置成功,则会通过他自身的发送端发出一些信息供查看,所以实现打开电脑的串口调试助手即可查看这些信息,串口调试助手的设置如下,具体的com编号因人而异。
这里我给出一个网盘链接供大家下载测试工具:https://pan.baidu.com/s/158qe4mcq5A6hvLvOTBK6vw 密码:5hxb
串口调试助手设置图
配置完成后开始传输数据,电脑事先需要打开网络调试助手,设置如下
网络调试助手配置图
这是top顶层文件代码
module ad9364_top(
input clk_100m,
output txdata,
input rxdata
)
clk_wiz clk_wiz_xst
(
// Clock out ports
.clk_40m(clk_40m), // output clk_40m
.clk_1p8432(clk_1p8432),
// Clock in ports
.clk_in(clk_100m)); // input clk_in
rst_generator rst_xst(
.clk_in(clk_40m),
.rst_n(rst_n)
);
fftdata fft_data_xst(
.clk(clk_1p8432),
.clkfftd(clk_40m),
.flag1(tx_rdy),
.fftdata(tx_data),
.reset(uart_rst),
.write(uart_wt),
.tx_data(uart_data),
.writebegin(writebegin)
);
uart uart_xst(
.mclkx16(clk_1p8432),
.reset(uart_rst),
.read(),
.write(uart_wt),
.data(),
.txdata(uart_data),
.sin(),
.sout(uart_sout),
.rxrdy(),
.txrdy(),
.parity_error(),
.framing_error(),
.overrun()
);
这是AT指令存储以及控制uart的模块代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//
// editor: loving_Qi
//
// Create Date: 2018/05/31 10:34:12
// Design Name:
// Module Name: control_udp
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module fftdata(input clk,
input clkfftd,
input flag1,
input [7:0]fftdata,
input rst_n,
output reset,
output write,
output[7:0] tx_data,
output writebegin
);
reg reset=0;
reg write;
reg writebegin=0;
reg flag=0;
reg readover=0;
reg [31:0] j=0;
reg [31:0] count=0;
reg [31:0] count1=0;
reg [7:0] tx_data;
reg [31:0] count_w=0;
reg [31:0] count_d=0;
reg [31:0] count_s=0;
reg [7:0] memo[4178:0];
//reg [7:0] memo1[2499999:0];
always@(posedge clkfftd )
begin
//if(!rst_n)
//begin
// j <= 0;
// flag <= 0;
//end
//else
//begin
memo[0]<=8'h41;
memo[1]<=8'h54;
memo[2]<=8'h2b;
memo[3]<=8'h52;
memo[4]<=8'h53;
memo[5]<=8'h54;
memo[6]<=8'h0d;
memo[7]<=8'h0a;
memo[8]<=8'h41;
memo[9]<=8'h54;
memo[10]<=8'h2b;
memo[11]<=8'h43;
memo[12]<=8'h57;
memo[13]<=8'h4d;
memo[14]<=8'h4f;
memo[15]<=8'h44;
memo[16]<=8'h45;
memo[17]<=8'h3d;
memo[18]<=8'h33;
memo[19]<=8'h0d;
memo[20]<=8'h0a;
memo[21]<=8'h41;
memo[22]<=8'h54;
memo[23]<=8'h2b;
memo[24]<=8'h43;
memo[25]<=8'h49;
memo[26]<=8'h50;
memo[27]<=8'h53;
memo[28]<=8'h54;
memo[29]<=8'h41;
memo[30]<=8'h52;
memo[31]<=8'h54;
memo[32]<=8'h3d;
memo[33]<=8'h22;
memo[34]<=8'h54;
memo[35]<=8'h43;
memo[36]<=8'h50;
memo[37]<=8'h22;
memo[38]<=8'h2c;
memo[39]<=8'h22;
memo[40]<=8'h31;
memo[41]<=8'h39;
memo[42]<=8'h32;
memo[43]<=8'h2e;
memo[44]<=8'h31;
memo[45]<=8'h36;
memo[46]<=8'h38;
memo[47]<=8'h2e;
memo[48]<=8'h34;
memo[49]<=8'h2e;
memo[50]<=8'h32;
memo[51]<=8'h22;
memo[52]<=8'h2c;
memo[53]<=8'h38;
memo[54]<=8'h30;
memo[55]<=8'h38;
memo[56]<=8'h34;
memo[57]<=8'h0d;
memo[58]<=8'h0a;
memo[59]<=8'h41;
memo[60]<=8'h54;
memo[61]<=8'h2b;
memo[62]<=8'h43;
memo[63]<=8'h49;
memo[64]<=8'h50;
memo[65]<=8'h4d;
memo[66]<=8'h4f;
memo[67]<=8'h44;
memo[68]<=8'h45;
memo[69]<=8'h3d;
memo[70]<=8'h31;
memo[71]<=8'h0d;
memo[72]<=8'h0a;
memo[73]<=8'h41;
memo[74]<=8'h54;
memo[75]<=8'h2b;
memo[76]<=8'h43;
memo[77]<=8'h49;
memo[78]<=8'h50;
memo[79]<=8'h53;
memo[80]<=8'h45;
memo[81]<=8'h4e;
memo[82]<=8'h44;
memo[83]<=8'h0d;
memo[84]<=8'h0a;
if(flag1==1)
begin
if(j<4094)
begin
memo[j+85]<=fftdata;
j<=j+1;
flag<=0;
end
else
begin
j<=0;
flag<=1;
end
end
else
begin
j<=0;
flag<=1;
end
end
//end
always@(posedge clk )
begin
//if(!rst_n)
// begin
// count1 <= 0;
// reset <=0;
// end
//else
// begin
if(count1>5&&count1<11)
begin
reset<=1;
count1<=count1+1;
end
else if(count1<6)
count1<=count1+1;
else
reset<=0;
end
//end
always@(posedge clk )
begin
//if(!rst_n)
//begin
// readover <= 0;
// count_w <= 0;
// count_d <= 0;
// count_s <= 0;
// write <= 1;
// writebegin <= 0;
//end
//else
//begin
if(flag==0)
begin
readover<=0;
count_w<=0;
end
else
begin
if(count_w<=7&&readover==0)
begin
write<=1'b1;
if(count_w==7)
begin
// tx_data<=(count_d<92)?memo[count_d]:memo1[count_d-1125];
tx_data<=memo[count_d];
if(count_d<86&&memo[count_d-1]==8'h0a && count_s<8000000)
begin
count_s<=count_s+1;
write<=1'b1;
end
else if(count_d==84)
begin
writebegin<=1;
count_d<=count_d+1;
count_w<=count_w+1;
end
else
begin
count_d<=count_d+1;
count_w<=count_w+1;
count_s<=0;
if (count_d>4178)
begin
count_d<=85;
count_w<=200;
readover<=1;
end
end
end
else
count_w<=count_w+1;
end
else if(count_w>7 &&count_w<=19&&readover==0)
begin
write<=1'b0;
count_w<=count_w+1;
end
else if(count_w>19 &&count_w<=178&&readover==0)
begin
write<=1'b1;
count_w<=count_w+1;
end
else if (count_w==179&&readover==0)
count_w<=0;
// tx_data=0;
end
end
//end
endmodule
下面是UART部分Verilog代码
UART的top文件
/******************************************************************
*
* File: uart.v
*
* Purpose: Top level UART description. UART implements
* a full duplex function. This interface
* interprets processor read/write parallel bus
* protocol and translates to serial interface.
*
**********************************************************************/
`timescale 1ns / 100ps
module uart ( mclkx16, reset, read, write, data, txdata,
sin, sout, rxrdy, txrdy, parity_error, framing_error, overrun);
input mclkx16; // Input clock, 16 x baud rate clock
input read; // read strobe input
input write; // write strobe input
input reset; // Master reset input
input [7:0] txdata; wire [7:0] txdata; //trans data
output [7:0] data; // Bidirectional data bus
// Receiver input signal, error and status flags
input sin; // Receive data line input from IrDA interface
output rxrdy; wire rxrdy; // Data ready to be read
output parity_error; wire parity_error; // Parity error flag
output framing_error; wire framing_error; // Framing error flag
output overrun; wire overrun; // Overrun error flag
wire [7:0] rxdata; // Intermediate output signals from receiver
// Transmitter output signal and status flag
output sout; wire sout; // Transmit data line output
output txrdy; wire txrdy; // Transmitter ready for next byte
//Instantiation of the transmitter module
txmit tx (mclkx16, write, reset, sout, txrdy, txdata);
// Instantiation of the receiver module
rxcver rx (mclkx16, read, sin, reset, rxrdy, parity_error, framing_error, overrun, rxdata);
// Drives the data bus during data read, otherwise tri-state the data bus
assign data = !read ? rxdata : 8'bzzzzzzzz;
endmodule
UART发送模块verliog代码
/******************************************************************
*
* File: txmit.v
*
* Purpose: UART transmit description. Interprets
* processor read/write parallel bus cycles
* and converts to output serial data.
*
**********************************************************************/
`timescale 1ns / 100ps
module txmit
(mclkx16, write, reset, sout, txrdy, data);
input mclkx16; // Input clock, 16 x baudrate clock
input write; // Transmit write signal
input reset; // Reset
output sout; reg sout; // Transmit data output
output txrdy; // Transmitter ready to recieve next byte to be send
input [7:0] data; // 8-bit input data bus
reg write1, write2; // Delayed write signals
reg txdone1; // txdone delayed signal
// Transmit shift register bits
reg [7:0] thr; // Transmit hold register
reg [7:0] tsr; // Transmit shift register, used for shifting out data to sout
reg tag1, tag2; // Tag bits used for detecting, when the tsr is empty
wire paritymode = 1'b1; // Initialized to 1 = odd parity, 0 = even parity
reg txparity; // Parity generation register
// Transmit clock and other control signals
reg txclk; // Transmit clock, i.e. baudrate clock = mclkx16 / 16
wire txdone; // Set to high, when shifting of byte is done
wire paritycycle; // Set to high, one cycle next to last shift cycle
reg txdatardy; // Set to high, when data is ready in transmit hold register
reg [2:0] cnt; // Counter used for generating the internal baud rate clock
// Paritycycle = 1 on next to last cycle, this means when tsr[1] gets tag2
assign paritycycle = tsr[1] && !(tag2 || tag1 || tsr[7] || tsr[6] || tsr[5] || tsr[4] || tsr[3] || tsr[2]);
// txdone = 1 when done shifting, this means when sout gets tag2
assign txdone = !(tag2 || tag1 || tsr[7] || tsr[6] || tsr[5] || tsr[4] || tsr[3] || tsr[2] || tsr[1] || tsr[0]);
// Ready for new data to be written, when no data is in transmit hold register
assign txrdy = !txdatardy;
// Latch data[7:0] into the transmit hold register at falling edge of write
always @(write or data)
if (~write)
thr = data;
// Toggle txclk every 8 counts, which divides the clock by 16, to generate the baud clock
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
txclk <= 1'b0;
cnt <= 3'b000;
end
else
begin
if (cnt == 3'b000)
txclk <= !txclk;
cnt <= cnt + 1;
end
// Shifting out data to sout
always @(posedge txclk or posedge reset)
if (reset)
begin
tsr <= 8'h00; // Reset transmit shift register
tag2 <= 1'b0; // Reset tag bit
tag1 <= 1'b0; // Reset tag bit
txparity <= 1'b0; // Reset txparty bit
sout <= 1'b1; // Idle -> set start bit high
end
else
begin
if (txdone && txdatardy)
begin
tsr <= thr; // Load thr to shift register
tag2 <= 1'b1; // Set tag bits for detecting when shifting is done
tag1 <= 1'b1; // Set tag bits for detecting when shifting is done
txparity <= paritymode; // Set parity mode -> 0 = even parity, 1 = odd parity
sout <= 1'b0; // Set start bit low
end
else
begin
tsr <= tsr >> 1; // Send LSB first
tsr[7] <= tag1; // Set tsr[7] = tag1
tag1 <= tag2; // Set tag1 = tag2
tag2 <= 1'b0; // Set tag2 = 0
txparity <= txparity ^ tsr[0]; // Generate parity
// Shift out data or parity bit or stop/idle bit.
if (txdone)
sout <= 1'b1; // Output stop/idle bit
else if (paritycycle)
sout <= txparity; // Output parity bit
else
sout <= tsr[0]; // Shift out data bit
end
end
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
txdatardy <= 1'b0;
write2 <= 1'b1;
write1 <= 1'b1;
txdone1 <= 1'b1;
end
else
begin
if (write1 && !write2)
txdatardy <= 1'b1; // Set txdatardy on rising edge of write
else if (!txdone && txdone1)
txdatardy <= 1'b0; // Falling edge of txdone indicated the thr is loaded in the tsr
// Generate delayed versions of write and txdone signals for edge detection.
write2 <= write1;
write1 <= write;
txdone1 <= txdone;
end
endmodule
UART接收模块verilog代码
/******************************************************************
*
* File: rxcver.v
*
* Purpose: Main UART receiver logic module. Receives
* incoming serial data and present parallel
* byte of data to system. Includes rxrdy control
* signals for handshaking of system bus. Includes
* control flags for parity, overrun data, and
* framing errors.
*
**********************************************************************/
`timescale 1ns / 100ps
module rxcver (mclkx16, read, sin, reset, rxrdy, parity_error, framing_error, overrun, rxdata);
input mclkx16; // Input clock, 16 x baudrate clock
input read; // Read control signal
input sin; // Receive input serial signal
input reset; // Reset
// receive status & error signals
output rxrdy; // Data received
output parity_error; reg parity_error; // Parity error control signal
output framing_error; reg framing_error; // Framing error detect signal
output overrun; reg overrun; // Overrun error detect signal
// 8 bit latched output data bus.
output [7:0] rxdata; reg [7:0]rxdata; // 8-bit output data bus
// Internal control signals.
reg [3:0] rxcnt; // Count of clock cycles
reg rx1, read1, read2, idle1, hunt; // Delayed version signals
// Receive shift register bits
reg [7:0] rhr; // Receive hold register
reg [7:0] rsr; // Receive shift register
reg rxparity; // Received parity bit
reg paritygen; // Parity generated from received data
reg rxstop; // Received data stop bit
// Receive clock and control signals.
reg rxclk; // Receive data shift clock
reg idle; // idle = 1 when receiver is idle
reg rxdatardy; // rsdatardy = 1 when data is ready to be read
// Idle signal enables rxclk generation - idle = 0 when not shifting data
// idle = 1 when low "rxstop" bit = rsr[0]
always @(posedge rxclk or posedge reset)
begin
if (reset)
idle <= 1'b1;
else
idle <= !idle && !rsr[0];
end
// Synchronizing rxclk to the centerpoint of low leading startbit
always @(posedge mclkx16)
begin
// A start bit is eight clock times with sin=0 after a falling edge of sin
if (reset)
hunt <= 1'b0;
else if (idle && !sin && rx1 )
hunt <= 1'b1; // Look for falling edge of sin
else if (!idle || sin )
hunt <= 1'b0; // Stop when shifting in data, or a 1 is found on sin
if (!idle || hunt)
rxcnt <= rxcnt + 1; // Count clocks when not idle, or looking for start bit
else
rxcnt <= 4'b0001; // Hold rxcnt = 1, when idle and waiting for falling edge of sin
rx1 <= sin; // Looking for falling edge detect on sin
rxclk <= rxcnt[3]; // rxclk = mclkx16 / 16
end
// When not idle, sample data at the sin input and create parity
always @(posedge rxclk or posedge reset)
if (reset)
begin
rsr <= 8'b11111111; // Initialize shift register
rxparity <= 1'b1; // Set to 1 -> for data shifting
paritygen <= 1'b1; // Set to 1 -> odd parity mode
rxstop <= 1'b0; // Controls idle = 1, when rsr[0] gets rxstop bit
end
else
begin
if (idle)
begin
rsr <= 8'b11111111; // Initialize shift register
rxparity <= 1'b1; // Set to 1 -> for data shifting
paritygen <= 1'b1; // Set to 1 -> odd parity mode
rxstop <= 1'b0; // Controls idle = 1, when rsr[0] gets rxstop bit
end
else
begin
rsr <= rsr >> 1; // Right shift sin shift register
rsr[7] <= rxparity; // Load rsr[7] with rxparity
rxparity <= rxstop; // Load rxparity with rxstop
rxstop <= sin; // Load rxstop with sin
paritygen <= paritygen ^ rxstop; // Generate running parity
end
end
// Generate status & error flags
always @(posedge mclkx16 or posedge reset)
if (reset)
begin
rhr <= 8'h00;
rxdatardy <= 1'b0;
overrun <= 1'b0;
parity_error <= 1'b0;
framing_error <= 1'b0;
idle1 <= 1'b1;
read2 <= 1'b1;
read1 <= 1'b1;
end
else
begin
// Look for rising edge of idle and update output registers
if (idle && !idle1)
begin
if (rxdatardy)
overrun <= 1'b1; // Overrun error, if previous data still in holding register
else
begin
overrun <= 1'b0; // No overrun error, since holding register is empty
rhr <= rsr; // Update holding register with contens of shift register
parity_error <= paritygen; // paritygen = 1, if parity error
framing_error <= !rxstop; // framing_error, if stop bit is not 1
rxdatardy <= 1'b1; // Data is ready for reading flag
end
end
// Clear error and data registers when data is read
if (!read2 && read1)
begin
rxdatardy <= 1'b0;
parity_error <= 1'b0;
framing_error <= 1'b0;
overrun <= 1'b0;
end
idle1 <= idle; // Edge detect on idle signal
read2 <= read1; // 2 cycle delayed version of read - edge detection
read1 <= read; // 1 cycle delayed version of read - edge detection
end
assign rxrdy = rxdatardy; // Receive data ready output signal
always @(read or rhr) // Latch data output when read goes low
if (~read)
rxdata = rhr;
endmodule