NIOS和ESP8266的联用
WIFI模块
C程序
在Quartus中,打开tools-Nios II Software Bulid Tools for Eclipse,创建好项目后,在Eclipse中编写C代码:
main.c
#include <stdio.h>
#include <sys/unistd.h>
#include <io.h>
#include <string.h>
#include "system.h"
#include "altera_avalon_uart_regs.h"
#include "alt_types.h"
#include "sys/alt_irq.h"
#include "myuart.h"
#include "esp8266.h"
char ts[128];
int main(void)
{
UART_ISR_Init(); //串口初始化
ESP8266_Init(); //wifi模块初始化
sprintf(ts,"AT+MQTTPUB=0,\"th180406\",\"{\\\"temperture\\\":%d.%d\\,\\\"humidity\\\":%d.%d}\",1,0\r\n",
29,3,14,5);
ESP_PUB(ts); //发布topic
while(1)
{
}
return 0;
}
myuart.c
#include "myuart.h"
unsigned char esp8266_buf[128];
unsigned short esp8266_cnt;
unsigned short esp8266_cntPre;
void UART_Irq_Handler(void)
{
alt_u8 rx_dat;
alt_u16 status;
//读取接收数据
do{
status = IORD_ALTERA_AVALON_UART_STATUS(UART_1_BASE);
}while(!(status & ALTERA_AVALON_UART_STATUS_RRDY_MSK));
rx_dat = IORD_ALTERA_AVALON_UART_RXDATA(UART_1_BASE);
if(esp8266_cnt >= sizeof(esp8266_buf))
esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++]=rx_dat;
}
void UART_ISR_Init(void)
{
//清除中断标志寄存器
IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0x0);
//注册中断
alt_ic_isr_register(UART_0_IRQ_INTERRUPT_CONTROLLER_ID,
UART_0_IRQ,
UART_Irq_Handler,
0x0,
0x0);
//允许UART接收中断
IOWR_ALTERA_AVALON_UART_CONTROL(UART_0_BASE, 0x0080);
}
/*
* 串口发送字符串函数
* */
void Uart_sendString(char *data, unsigned char len)
{
alt_u8 i;
for(i=0;i<len;i++)
{
while(!(IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE)&
ALTERA_AVALON_UART_STATUS_TRDY_MSK));
IOWR_ALTERA_AVALON_UART_TXDATA(UART_0_BASE, data[i]);
}
}
esp8266.c
#include "esp8266.h"
#include "myuart.h"
#include <stdio.h>
#include <sys/unistd.h>
#include <string.h>
#define ESP8266_WIFI_INFO "AT+CWJAP=\"WIFI名称\",\"密码\"\r\n"
#define ESP8266_MQTT_USERCFG "AT+MQTTUSERCFG=0,1,\"ESP32\",\"espressif\",\"1234567890\",0,0,\"\"\r\n"
#define ESP8266_MQTT_CONN "AT+MQTTCONN=0,\"服务器域名\",端口号,0\r\n"
#define ESP8266_MQTT_SUB1 "AT+MQTTSUB=0,\"主题1\",1\r\n"
#define ESP8266_MQTT_SUB2 "AT+MQTTSUB=0,\"主题2\",1\r\n"
extern unsigned char esp8266_buf[128];
extern unsigned short esp8266_cnt;
extern unsigned short esp8266_cntPre;
//==========================================================
// 函数名称: ESP8266_Init
//
// 函数功能: 初始化ESP8266
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Init(void)
{
//ESP8266_Clear();
printf("1.AT+RST\n"); //向串口1输出信息
while(ESP8266_SendCmd("AT+RST\r\n", "OK")) //向esp8266发送指令
usleep(500000);//delay 500ms
printf("2. CWMODE\n");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
usleep(500000);
printf("3. AT+CWDHCP\r\n");
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
usleep(500000);
printf("4. CWJAP\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
usleep(500000);
//用户信息配置
printf("5. AT+MQTTUSERCFG\n");
while(ESP8266_SendCmd(ESP8266_MQTT_USERCFG, "OK"))
usleep(500000);
//连接MQTT服务器
printf("6. AT+MQTTCONN\n");
while(ESP8266_SendCmd(ESP8266_MQTT_CONN, "MQTTCONNECTED"))
usleep(500000);
//订阅主题
printf("7. AT+MQTTSUB1\n");
while(ESP8266_SendCmd(ESP8266_MQTT_SUB1, "OK"))
usleep(500000);
//订阅主题
printf(" AT+MQTTSUB2\n");
while(ESP8266_SendCmd(ESP8266_MQTT_SUB2, "OK"))
usleep(500000);
printf("8. ESP8266 Init OK\n");
}
//==========================================================
// 函数名称: ESP8266_WaitRecive
//
// 函数功能: 等待接收完成
//
// 入口参数: 无
//
// 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
//
// 说明: 循环调用检测是否接收完成
//==========================================================
alt_u8 ESP8266_WaitRecive(void)
{
if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return REV_WAIT;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
{
esp8266_cnt = 0; //清0接收计数
return REV_OK; //返回接收完成标志
}
esp8266_cntPre = esp8266_cnt; //置为相同
return REV_WAIT; //返回接收未完成标志
}
//==========================================================
// 函数名称: ESP8266_SendCmd
//
// 函数功能: 发送命令
//
// 入口参数: cmd:命令
// res:需要检查的返回指令
//
// 返回参数: 0-成功 1-失败
//
// 说明:
//==========================================================
unsigned char ESP8266_SendCmd(char *cmd, char *res)
{
unsigned char timeOut = 200;
Uart_sendString(cmd, strlen((const char *)cmd)); //向串口0发送数据
while(timeOut--)
{
if(ESP8266_WaitRecive() == REV_OK)
{
if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
ESP8266_Clear(); //清空缓存
return 0;
}
}
usleep(10000); //delay 10ms
}
return 1;
}
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
//主题发布
void ESP_PUB(char *cmd)
{
while(ESP8266_SendCmd(cmd, "SUBRECV"))
usleep(500000);
}
Verilog代码
定义了两个串口,只使用了串口0。
module uart_test(
clk,
rst_n,
uart0_rx,
uart0_tx,
uart1_rx,
uart1_tx
);
input clk;
input rst_n;
input uart0_rx;
output uart0_tx;
input uart1_rx;
output uart1_tx;
uart_qsys u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (rst_n), // reset.reset_n
.uart0_external_rxd (uart0_rx), // uart0_external.rxd
.uart0_external_txd (uart0_tx), // .txd
.uart1_external_rxd (uart1_rx), // uart1_external.rxd
.uart1_external_txd (uart1_tx) // .txd
);
endmodule
引脚定义:
连线:
esp-01s | FPGA(EP4C6F17C8) |
---|---|
3V3 | 3V3 |
GND | GND |
RX | M12(uart0_tx) |
TX | N16(uart0_rx) |
测试
在Eclipse中运行程序,当ESP8266做好相关配置,则会打印出如下信息:
![](https://img2023.cnblogs.com/blog/2126010/202302/2126010-20230223170658551-1892021421.png)
ESP8266连接手机热点:
![](https://img2023.cnblogs.com/blog/2126010/202302/2126010-20230223170658962-539655649.jpg)
在MQTTX中检测FPGA发来的数据:
![](https://img2023.cnblogs.com/blog/2126010/202302/2126010-20230223170659454-1847790405.png)
如图测试成功。
总结
大部分的代码是从stm32移植过来的,基本只做了串口的改动。由于FPGA上的ram资源不多,在编写代码应该尽量减少代码量。
参考:https://www.bilibili.com/video/BV15t411u7ws?p=7&vd_source=1c653b4d6d3f8ae1c834471e7fd829f0