该模块可用于FPGA驱动ESP01-s连接MQTT服务器,同时支持使用HC-05蓝牙模块对ESP01-s完成配网操作,并且接收上位机指令,并发送信息至MQTT服务器。

效果演示:

使用Android APP向FPGA发送控制指令控制LED状态。

ESP-01s:

ESP-01S是由安信可科技开发的一款Wi-Fi模块。其核心处理器是ESP8266,该处理器在较小尺寸的封装中集成了业界领先的Tensilica L106超低功耗32位微型MCU,带有16位精简模式,主频支持80MHz和160MHz,并集成了Wi-Fi MAC/BB/RF/PA/LNA。

ESP-01s(MQTT)固件烧录:

前期准备:

USB转TTL模块或ESP01s下载器。

安信可flash_download_tools:下载地址

固件使用安信可科技MQTT透传AT固件(固件号:1471)下载地址

固件烧录详细步骤

ESP01s初始化模块(ESP01_Init.v):

该模块用于对ESP01s进行上电初始化以及驱动HC-05蓝牙模块向ESP01-s发送SSID及密码信息。其格式为:GetWIFI:AT+CWJAP="SSID","PASS"\n,当WiFi连接成功时,用蓝牙模块发送字符“T”,反之发送字符“F”。

ESP01_Init.

//基于Verilog的ESP32-01s初始化模块
module ESP01_Init (
    input clk               ,
    input rst               ,
    input bt_rx             ,
    input esp_rx            ,
    output test_rx           ,
    input esp_init_start    ,
    output bt_tx            ,
    output test_tx          ,
    output reg esp_tx           ,
    output reg esp_init_done    ,
    output reg get_wifi_done 
);
assign test_rx = esp_tx;
assign test_tx = esp_rx;
//状态机
reg [3:0] state;     //状态机状态
reg [3:0] next_state;//下一个状态
parameter init_     = 4'b0000;//初始化状态
parameter wait_     = 4'b0001;//等待状态
parameter get_wifi  = 4'b0010;//获取WIFI状态
parameter set_wifi  = 4'b0100;//设置WIFI状态
parameter if_line   = 4'b0101;//判断行数状态
parameter set_done  = 4'b0111;//设置完成状态
parameter pw_str1   = 4'b1000;//ESP01s初始化字符串1
parameter pw_str1_tx= 4'b1001;//ESP01s初始化字符串1发送
parameter pw_str2   = 4'b1010;//ESP01s初始化字符串2
parameter pw_str2_tx= 4'b1011;//ESP01s初始化字符串2发送
parameter pw_str3   = 4'b1100;//ESP01s初始化字符串3
parameter pw_str3_tx= 4'b1101;//ESP01s初始化字符串3发送
parameter pw_str4   = 4'b1110;//ESP01s初始化字符串4
parameter pw_str4_tx= 4'b1111;//ESP01s初始化字符串4发送
//AT+MQTTUSERCFG=0,1,"ESP01S","","",0,0,""

parameter esp_pw_str1 = "AT";
parameter esp_pw_str2 = "AT+MQTTUSERCFG=0,1,\"ESP01S\",\"\",\"\",0,0,\"\"";
parameter esp_pw_str3 = "AT+MQTTCONN=0,\"43.143.168.95\",1883,1";
parameter esp_pw_str4 = "AT+MQTTSUB=0,\"FPGA_IoT\",0";

reg timer_en;       //定时器使能
reg OK_rst;         //OK复位标志位
reg set_wifi_str;   //设置WIFI字符串标志位
reg line_flag;      //连接成功标志位
reg [7:0]bt_ctrl_data; //蓝牙串口发送数据
reg bt_uart_start;    //蓝牙串口发送开始标志位

wire  [(8*80)-1:0] WIFI_ID_PW; //WIFI账号密码
reg   [(8*80)-1:0] ESP_TX_Str; //WIFI账号密码

wire esp_wifi_tx; //ESP串口发送标志位

always @(posedge clk or negedge rst) begin
    if(!rst)begin
        state <= init_;
    end else begin
        state <= next_state;
    end 
end
reg [2:0] error_cnt; //错误计数
always @(*)begin
    case(state)
        init_:begin
            bt_ctrl_data = "F";
            esp_init_done = 1'b0;
            OK_rst = 1'b1;
            line_flag = 1'b0;
            error_cnt = 3'b000;
            bt_uart_start = 1'b0;
            timer_en  = 1'b1;
            set_wifi_str  = 1'b0;
            esp_tx = esp_wifi_tx;
            next_state = pw_str1;

        end
        pw_str1 :begin
            esp_init_done = 1'b0;
            set_wifi_str  = 1'b0;
            timer_en = 1'b0;
            esp_tx = esp_wifi_tx;
            ESP_TX_Str = esp_pw_str1;
            if(timer_5s)begin
                next_state = pw_str1_tx;
            end else begin
                next_state = pw_str1;
            end
        end
        pw_str1_tx :begin
            esp_init_done = 1'b0;
            set_wifi_str = 1'b1;
            timer_en = 1'b1;
            esp_tx = esp_wifi_tx;
            next_state = pw_str2;
        end
        pw_str2 :begin
            esp_init_done = 1'b0;
            set_wifi_str = 1'b0;
            timer_en = 1'b0;
            esp_tx = esp_wifi_tx;
            ESP_TX_Str = esp_pw_str2;
            if(timer_5s)begin
                next_state = pw_str2_tx;
            end else begin
                next_state = pw_str2;
            end
        end
        pw_str2_tx :begin
            esp_init_done = 1'b0;
            timer_en = 1'b1;
            set_wifi_str = 1'b1;
            esp_tx = esp_wifi_tx;
            next_state = pw_str3;
        end
        pw_str3 :begin
            esp_init_done = 1'b0;
            set_wifi_str = 1'b0;
            timer_en = 1'b0;
            esp_tx = esp_wifi_tx;
            ESP_TX_Str = esp_pw_str3;
            if(timer_5s)begin
                next_state = pw_str3_tx;
            end else begin
                next_state = pw_str3;
            end
        end
        pw_str3_tx :begin
            esp_init_done = 1'b0;
            timer_en = 1'b1;
            set_wifi_str = 1'b1;
            esp_tx = esp_wifi_tx;
            next_state = pw_str4;
        end
        pw_str4 :begin
            esp_init_done = 1'b0;
            set_wifi_str = 1'b0;
            timer_en = 1'b0;
            esp_tx = esp_wifi_tx;
            ESP_TX_Str = esp_pw_str4;
            if(timer_5s)begin
                next_state = pw_str4_tx;
            end else begin
                next_state = pw_str4;
            end
        end
        pw_str4_tx :begin
            esp_init_done = 1'b0;
            timer_en = 1'b1;
            set_wifi_str = 1'b1;
            esp_tx = esp_wifi_tx;
            next_state = wait_;
        end

        wait_:begin
            esp_init_done = 1'b1;
            bt_uart_start = 1'b0;
            bt_ctrl_data = "F";
            esp_tx = esp_wifi_tx;
            timer_en = 1'b1;
            set_wifi_str  = 1'b0;
            line_flag = 1'b0;
            OK_rst = 1'b1;
            
            if(!esp_init_start)begin
                next_state = get_wifi;
            end else begin
                next_state = wait_;
            end
        end
        get_wifi:begin
            OK_rst = 1'b1;
            bt_uart_start = 1'b0;
            timer_en = 1'b1;
            line_flag = 1'b0;
            set_wifi_str  = 1'b0;
            esp_tx = esp_wifi_tx;
            if(param_rec_done)begin
                next_state = set_wifi;
                ESP_TX_Str = WIFI_ID_PW;
            end else begin
                next_state = get_wifi;
            end 
        end
        set_wifi:begin
            OK_rst = 1'b1;
            timer_en = 1'b1;
            ESP_TX_Str = WIFI_ID_PW;
            set_wifi_str  = 1'b1;
            next_state = if_line;
            line_flag = 1'b0;
            esp_tx = esp_wifi_tx;
        end
        if_line:begin
            OK_rst = 1'b0;
            timer_en <= 1'b0;
            set_wifi_str  <= 1'b0;
            esp_tx = esp_wifi_tx;
            if(equal_flag)begin
                line_flag   = 1'b1;
                
                next_state = set_done;
            end else begin
                line_flag   = 1'b0;
                if(timer_5s)begin
                    error_cnt = error_cnt + 1;

                    if(error_cnt == 3'b111)begin
                        next_state = set_done;
                    end else begin
                        next_state = set_wifi;
                    end
                    
                end else begin
                    next_state = if_line;
                end
            end
        end
        set_done:begin
            error_cnt = 3'b000;
            esp_tx = esp_wifi_tx;
            OK_rst = 1'b1;
            timer_en <= 1'b1;
            next_state = wait_;
            if(line_flag)begin
                bt_ctrl_data = "T";
                bt_uart_start = 1'b1;
            end else begin
                bt_ctrl_data = "F";
                bt_uart_start = 1'b1;
            end
        end
    endcase
end


wire [7:0] bt_rx_data; //蓝牙串口接收数据
//蓝牙串口--->BT串口
    //集串口发送和串口接收于一体的串口模块,
    //tx与rx端口连接外部串口引脚,rx_data为串口接收到的数据,
    //rx_rec_flag为串口接收到数据标志位
    //tx_start为串口发送开启信号,tx_done为串口发送完成信号,
    uart_const_baud #(.clock_freq(50_000_000),.baud_rate(115200)) Uart_BT(
        .rx_data    (bt_rx_data     ),  //串口接收到的数据
        .rx_rec_flag(bt_rx_rec_flag ),  //串口接收到数据标志位
        .tx         (bt_tx          ),  //串口发送引脚
        .tx_done    (bt_tx_done     ),  //串口发送完成信号
        .tx_idle    (bt_tx_idle     ),  //串口发送空闲信号
        .tx_data    (bt_ctrl_data   ),  //串口发送数据
        .tx_start   (bt_uart_start  ),  //串口发送开启信号
        .rx         (bt_rx          ),  //串口接收引脚
        .rx_clr     (bt_rx_rec_flag ),  //串口接收到数据标志位清零
        .clk        (clk            ),  //时钟
        .rst        (!rst            )   //复位
    );
    //获取对应的字符串参数,通过一对括号"("和")"来界定参数内容
    //如LED0(1000),此时会将1000这串字符串筛选出来
    //其中,str_buf为顶格,我们直接用排列后的str_rank
    string_param #(.byte_num(80),.start_signal(":"),.end_signal(16'h0d)) STR_SAVE0(
        .str_buf        (),
        .str_rank       (WIFI_ID_PW     ),
        .param_rec_done (param_rec_done ),
        .str_in         (bt_rx_data     ),
        .rx_flag        (bt_rx_rec_flag ),
        .clk            (clk            ),
        .rst            (!rst           )
        );

//esp32串口--->ESP串口
    wire [7:0] esp_ctrl_data;  //ESP串口发送数据
    wire [7:0] esp_rx_data;    //ESP串口接收数据
    //集串口发送和串口接收于一体的串口模块,
    //tx与rx端口连接外部串口引脚,rx_data为串口接收到的数据,
    //rx_rec_flag为串口接收到数据标志位
    //tx_start为串口发送开启信号,tx_done为串口发送完成信号,
    uart_const_baud #(.clock_freq(50_000_000),.baud_rate(115200)) UART_esp(
        .rx_data    (esp_rx_data    ),
        .rx_rec_flag(esp_rx_rec_flag),
        .tx         (esp_wifi_tx    ),
        .tx_done    (esp_tx_done    ),
        .tx_idle    (esp_tx_idle    ),
        .tx_data    (esp_ctrl_data  ),
        .tx_start   (esp_start      ),
        .rx         (esp_rx         ),
        .rx_clr     (esp_rx_rec_flag),
        .clk        (clk            ),
        .rst        (!rst           )
    );
    
    //定时器,通过给定与时钟频率一致的分频值,生成一秒定时脉冲
    //定时脉冲连至串口发送字符串模块启动端,产生每秒发送一次的功能
    timer #(.div_value(500_000_000)) TIMER0(
        .timeout(timer_5s),
        .clk(clk),
        .rst(timer_en)
    );

    //串口发送字符串模块,string_in端口输入要发送的内容
    //byte_num计算方法:每个中文文字占两位,每个英文字符占一位,回车换行(16'h0d0a)占两位。
    uart_string_send #(.byte_num(82),.skip_end(1)) UART_SEND_esp(
        .uart_data      (esp_ctrl_data          ),
        .uart_start     (esp_start              ),
        .str_in         ({ESP_TX_Str,16'h0d0a}           ),
        .uart_tx_done   (esp_tx_done            ),
        .uart_tx_idle   (esp_tx_idle            ),
        .send_start     (set_wifi_str           ),
        .clk            (clk                    ),
        .rst            (!rst                   )
        );

    //字符串保存对比模块,start_signal为开始识别信号,保存内容为开始信号后的内容
    //如开始信号为".",则当输入.12345时就会保存内容12345并进行对比
    //str_cmp为进行对比的字符串内容
    string_save_cmp #(.byte_num(2),.start_signal(16'h0a)) STR_CMP0(
        .equal(equal_flag),
        .str_cmp("OK"),
        .str_in(esp_rx_data),
        .rx_flag(esp_rx_rec_flag),
        .clk(clk),
        .rst(OK_rst)
        );
endmodule

ESP01s顶层驱动模块(ESP01_Drive.v):

该模块为顶层模块,用于例化其他子模块以及对其他子模块控制。

ESP01_Drive.v

//基于Verilog的 ESP01 mqtt固件驱动程序

module ESP01_Drive (
    input clk,
    input rst,

    //ESP01端口
    input esp_rx,
    output esp_tx,

    //蓝牙模块端口
    input  bt_rx,
    output bt_tx,

    //数据输入输出端口
    input  [39:0] value1,
    input  [39:0] value2,
    input  [39:0] value3,
    input  [39:0] value4,

    //用户输入端口
    input  get_wifi,     //配网状态

    //测试端口
    output test_rx,
    output test_tx
    
);

assign test_rx = esp_rx;
assign test_tx = esp_tx;
//状态机
reg [3:0] state;     //状态机状态
reg [3:0] next_state;//下一个状态

parameter init_     = 4'b0000;//初始化ESP01状态
parameter wait_     = 4'b0001;//等待状态
parameter get_wifi_ = 4'b0010;//配网状态

reg tx_cs; //发送数据标志
assign esp_tx = tx_cs ?  mqtt_tx_tmp : init_tx_tmp;

reg   [(8*80)-1:0] ESP_TX_Str; //WIFI账号密码

wire init_tx_tmp,mqtt_tx_tmp;

always @(posedge clk or negedge rst) begin
    if(!rst)begin
        state <= init_;
    end else begin
        state <= next_state;
    end 
end

always @(*)begin
    case(state)

        init_:begin //初始化ESP01
            tx_cs = 1'b0;
            
            if(esp_init_done)begin
                next_state = wait_;
            end else begin
                next_state = init_;
            end
        end
        wait_:begin //等待状态
            tx_cs = 1'b1;
            if(!get_wifi)begin //按下配网键
                next_state = get_wifi_;
            end else begin
                next_state = wait_;
            end
        end



        get_wifi_:begin //配网状态
            tx_cs = 1'b0;
            if(get_wifi_done)begin
                next_state = wait_;
            end else begin
                next_state = get_wifi_;
            end
        end

    endcase 
end
//例化ESP01初始化模块
ESP01_Init ESP01_Init_0(
    .clk            (clk            ),
    .rst            (rst            ),
    .bt_rx          (bt_rx          ),
    .esp_rx         (esp_rx         ),
    .test_rx        (        ),
    .esp_init_start (get_wifi       ),
    .bt_tx          (bt_tx          ),
    .test_tx        (        ),
    .esp_tx         (init_tx_tmp    ),
    .esp_init_done  (esp_init_done  ),
    .get_wifi_done  (get_wifi_done  )
);

ESP01_set ESP01_set0(
    .clk    (clk        ),
    .rst    (rst        ),
    .value1 (value1     ),
    .value2 (value2     ),
    .value3 (value3     ),
    .value4 (value4     ),
    .esp_tx (mqtt_tx_tmp)
);

endmodule

ESP01sMQTT发送模块(ESP01_set.v):

该模块用于向上位机发送MQTT消息。发送格式为:“ "T1": "30.6",\n"T2": "43.5",\n"T3": "null",\n"T4": "null"

ESP01_set.v

module ESP01_set (
    input clk               ,
    input rst               ,
    //数据输入输出端口
    input  [39:0] value1,
    input  [39:0] value2,
    input  [39:0] value3,
    input  [39:0] value4,
    output esp_tx            
);

    wire[7:0] uart_ctrl_data;
    wire [(126*8)-1:0] Set_TX_Str = {
        "AT+MQTTPUB=0,\"FPGA_IoT\",\"",
        "{",
        "\"T1\": \"",value1,"\",",
        "\"T2\": \"",value2,"\",",
        "\"T3\": \"",value3,"\",",
        "\"T4\": \"",value4,"\"",
        "}\",0,0"
    };
    //定时器,通过给定与时钟频率一致的分频值,生成一秒定时脉冲
    //定时脉冲连至串口发送字符串模块启动端,产生每秒发送一次的功能
    timer #(.div_value(50_000_000)) TIMER0(
        .timeout(timer_1s),
        .clk(clk),
        .rst(!rst)
    );
    
    //串口发送驱动,需要给定时钟频率和波特率
    uart_const_baud_tx #(.clock_freq(50_000_000),.baud_rate(115200)) UART_TX0(
        .tx(esp_tx),  
        .tx_done(uart_tx_done),
        .tx_idle(uart_tx_idle),
        .tx_start(uart_start),
        .tx_data(uart_ctrl_data),
        .clk(clk),
        .rst(!rst)
    );
    
    //串口发送字符串模块,string_in端口输入要发送的内容
    //byte_num计算方法:每个中文文字占两位,每个英文字符占一位,回车换行(16'h0d0a)占两位。
    //如果是使用了压缩包内的文件,那么此时编码方式为utf-8,此时一个中文字符占三位
    //所以如果是quartus或者gaoyun等utf-8的编码EDA工具,byte_num应改为26
    uart_string_send #(.byte_num(128),.skip_end(1)) UART_SEND0(
        .uart_data(uart_ctrl_data),
        .uart_start(uart_start),
        .str_in({Set_TX_Str,16'h0d0a}),
        .uart_tx_done(uart_tx_done),
        .uart_tx_idle(uart_tx_idle),
        .send_start(timer_1s),
        .clk(clk),
        .rst(!rst)
        );
endmodule

ESP01sMQTT接收模块(ESP01_Drive.v):

该模块用于接收上位机发送的MQTT消息。需要上位机按指定格式发送指令,指令格式:“[Switch]": "(00000000)”。

ESP_Str_Get1

//基于Verilog的字符串处理模块
/*
Get
{
  "[Switch]": "(00000000)"
}
set
{
  "temperature": "30.6",
  "humidity": "43.5",
  "textView4": "null",
  "textView5": "null"
}
*/
module ESP_Str_Get1#(
    parameter   Set_Str_num     = 10   ,   //发送字符串长度
                Get_Str_type    = 6     ,   //接收字符串标签长度
                Get_Str_value   = 8         //接收字符串值长度
)(
    input   clk     ,
    input   rst     ,

    input   esp_rx  ,

    output  wire [Get_Str_type*8-1:0] Get_type,
    output  wire [Get_Str_value*8-1:0] Get_value,
    output  wire get_done

);
wire [7:0] tx_data;
wire [7:0] rx_data;
//reg  [Set_Str_num*8-1:0]   Set_Str  ;
//wire [Get_Str_type*8-1:0]  Get_type ;
//wire [Get_Str_value*8-1:0] Get_value;
//例化Uart收发模块
uart_const_baud #(
    .clock_freq (50_000_000 ),
    .baud_rate  (115200     )
) Uart0(
    .rx_data        (rx_data        ),
    .rx_rec_flag    (rx_rec_flag    ),
    .tx             (         ),
    .tx_idle        (tx_idle        ),
    .tx_done        (tx_done        ),
    .tx_data        (tx_data        ),
    .tx_start       (tx_start       ),
    .rx             (esp_rx         ),
    .rx_clr         (rx_clr         ),
    .clk            (clk            ),
    .rst            (!rst           )
);

string_param #(.byte_num(Get_Str_type),.start_signal("["),.end_signal("]")) STR_SAVE_type(
    .str_buf        (Get_type       ),
    //.str_rank       (),
    .str_cnt        (),
    .param_rec_done (               ),
    .str_in         (esp_rx         ),
    .rx_flag        (rx_rec_flag    ),
    .clk            (clk            ),
    .rst            (!rst           )
);
string_param #(.byte_num(Get_Str_type),.start_signal("("),.end_signal(")")) STR_SAVE_view(
    .str_buf        (Get_value      ),
    //.str_rank       (),
    .str_cnt        (),
    .param_rec_done (get_done       ),
    .str_in         (esp_rx         ),
    .rx_flag        (rx_rec_flag    ),
    .clk            (clk            ),
    .rst            (!rst           )
);
    //将16位8421bcd码转为12位hex码,
    bcd_to_hex #(.bcd_bit(16), .hex_bit(12)) BTH0(
        .hex(rx_hex),
        .bcd(rx_bcd)
    );
endmodule

Verilog模块库:

该模块使用了B站博主晴空-Tiso的Verilog模块库:B站工房链接

此作者没有提供个人介绍
最后更新于 2024-11-23