通过UART0实现数据透传,将UART0的RX数据通过TCP传输到指定IP,将接受到其他sta发送的TCP数据,通过URAT0的TX打印出来。
开发环境:win10+官方提供VirtualBox和OVA镜像
编辑工具:source insight3.5
一、串口数据TCP透传
上一篇中用的是任务模式,但是有一个问题就是会一直阻塞到串口那里,其他任务无法正常运行,所以这次使用串口中断实现透传。在中断中无法直接使用socket发送数据,这里实现的在中断中开辟一个新任务,任务简单执行一个write即可,代码如下:
void socket_TCP_send( void * pvParameters )
{
ETS_UART_INTR_DISABLE();
//print_data(pvParameters);
write(sock_fd, pvParameters, strlen(pvParameters));
memset(TC_data,0,sizeof(TC_data));
ETS_UART_INTR_ENABLE();
vTaskDelete(NULL);
}
在SDK源码中的串口中断函数里是将接受到的数据原封不动的通过UART0的TX打印出来,接受一个打印一个,这里需要将接受到的数据装到一个全局变量里,以供发送使用,局部也可以,全局的好处在于可以控制接受任意字节的数据,进行透传。代码如下:
LOCAL void
uart0_rx_intr_handler(void *para)
{
/* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents
* uart1 and uart0 respectively
*/
uint8 RcvChar;
uint8 uart_no = UART0;//UartDev.buff_uart_no;
uint8 fifo_len = 0;
uint8 buf_idx = 0;
uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
while (uart_intr_status != 0x0) {
if (UART_FRM_ERR_INT_ST == (uart_intr_status & UART_FRM_ERR_INT_ST)) {
//printf("FRM_ERR\r\n");
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
} else if (UART_RXFIFO_FULL_INT_ST == (uart_intr_status & UART_RXFIFO_FULL_INT_ST)) {
printf("full\r\n");
fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
buf_idx = 0;
while (buf_idx < fifo_len) {
//uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
TC_data[buf_idx] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
buf_idx++;
}
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);
} else if (UART_RXFIFO_TOUT_INT_ST == (uart_intr_status & UART_RXFIFO_TOUT_INT_ST)) {
printf("tout\r\n");
fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
buf_idx = 0;
while (buf_idx < fifo_len) {
//uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
TC_data[buf_idx] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
buf_idx++;
}
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR);
} else if (UART_TXFIFO_EMPTY_INT_ST == (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST)) {
printf("empty\n\r");
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
} else {
//skip
}
uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
}
if(strlen(TC_data) > 0)
{
xTaskCreate(
socket_TCP_send, /* Function that implements the task. */
"socket_TCP_send", /* Text name for the task. */
512, /* Stack size in words, not bytes. */
TC_data, /* Parameter passed into the task. */
2,/* Priority at which the task is created. */
&xuHandle ); /* Used to pass out the created task's handle. */
}
}
全局变量定义如下:
char TC_data[256] = {0};
xTaskHandle xuHandle = NULL;
这样做的话就可以让串口不阻塞任务了。
二、将TCP数据透传到串口
在user_init函数中,开辟新的任务,这个任务负责创建socket实现TCPserver,然后将连接到server的client数据通过URAT0的TX输出,新任务代码如下:
void task_wifi_urat( void * pvParameters )
{
int ret = 0;
int clicoon = 0;//在server中的client连接套接字
do
{
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
vTaskDelay(1000/portTICK_RATE_MS);
}
}while(listenfd < 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9000);
servaddr.sin_addr.s_addr = INADDR_ANY;
do
{
ret = bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret != 0)
{
vTaskDelay(1000/portTICK_RATE_MS);
}
}while(ret != 0);
do
{
ret = listen(listenfd, 5);
if (ret != 0)
{
vTaskDelay(1000/portTICK_RATE_MS);
}
}while(ret != 0);
//print_data("ready TCP\n");
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
while(1)
{
clicoon = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if(clicoon < 0)
{
vTaskDelay(1000/portTICK_RATE_MS);
continue;
}
//printf("accept success\n");
while(1)
{
memset(tcp_msg, 0, sizeof(tcp_msg));
int ret = read(clicoon, tcp_msg, sizeof(tcp_msg));
if(ret <= 0)
{
//printf("close coon\n");
close(clicoon);
break;
}
else
{
print_data(tcp_msg);
}
}
}
}
由于ESP8266默认打印串口日志的是URAT0,但是这里的UART0是不需要这些没用的数据的, 所以需要在串口初始化的时候指定打印数据到UART1,然后手动的将TCP数据输出到UART0输出,输出函数代码如下:
void print_data(const char *data)
{
int i = 0;
for(i = 0; data[i] != 0; i++)
{
uart0_write_char(data[i]);
}
}
在初始化中将UART1指定为日志打印代码:
UART_SetPrintPort(UART1);
这样就可以实现双向透传了,如果有更简单的方法,希望大神们多多指教。