学习过程中参考博文:
记录——基于 RT-Thread 实现 USB 虚拟串口
但是遇到了两个问题:
1.串口有设备,但是不能发送;
2.串口能发送但不能接收;
第一个问题,是因为rtt的usb虚拟串口默认启用了RTS和DTR(终端准备好接收):
static rt_err_t _interface_handler(ufunction_t func, ureq_t setup)
{
struct vcom *data;
RT_ASSERT(func != RT_NULL);
RT_ASSERT(func->device != RT_NULL);
RT_ASSERT(setup != RT_NULL);
data = (struct vcom*)func->user_data;
switch(setup->bRequest)
{
case CDC_SEND_ENCAPSULATED_COMMAND:
break;
case CDC_GET_ENCAPSULATED_RESPONSE:
break;
case CDC_SET_COMM_FEATURE:
break;
case CDC_GET_COMM_FEATURE:
break;
case CDC_CLEAR_COMM_FEATURE:
break;
case CDC_SET_LINE_CODING:
_cdc_set_line_coding(func->device, setup);
break;
case CDC_GET_LINE_CODING:
_cdc_get_line_coding(func->device, setup);
break;
case CDC_SET_CONTROL_LINE_STATE:
data->connected = (setup->wValue & 0x01) > 0?RT_TRUE:RT_FALSE;
RT_DEBUG_LOG(RT_DEBUG_USB, ("vcom state:%d \n", data->connected));
dcd_ep0_send_status(func->device->dcd);
break;
case CDC_SEND_BREAK:
break;
default:
rt_kprintf("unknown cdc request\n",setup->request_type);
return -RT_ERROR;
}
return RT_EOK;
}
参考RS协议232控制定义:
代码中的data->connected = (setup->wValue & 0x01) > 0?RT_TRUE:RT_FALSE;
中的0x01代表检查DTR,0x02代表检查RTS,如果不需要DTR或者RTS可以直接把这里的data->connected
置1。
不介意的话就可以在串口工具上勾上DTR:
第二个接收不到数据的问题,main.c是这样写的:
#include
#include
rt_device_t dev = RT_NULL;
static struct rt_semaphore rx_sem;
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
rt_kprintf("rx:%d...\n", size);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
char ch;
while (1)
{
/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
while (rt_device_read(dev, -1, &ch, 1) != 1)
{
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
rt_kprintf("read:%d\n", ch);
}
}
int main(void)
{
char buf[] = "hello rt-thread!\r\n";
dev = rt_device_find("vcom");
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
if (dev)
rt_device_open(dev, RT_DEVICE_FLAG_RDWR);
else
return -RT_ERROR;
rt_device_set_rx_indicate(dev, uart_input);
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
rt_thread_startup(thread);
while (1)
{
rt_device_write(dev, 0, buf, rt_strlen(buf));
rt_thread_mdelay(500);
}
return RT_EOK;
}
实际情况是会调用uart_input
,但得到的size却一直都是-979,Debug代码发现在函数void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
中,有如下代码:
case RT_SERIAL_EVENT_RX_IND:
{
int ch = -1;
rt_base_t level;
struct rt_serial_rx_fifo* rx_fifo;
/* interrupt mode receive */
rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
RT_ASSERT(rx_fifo != RT_NULL);
while (1)
{
ch = serial->ops->getc(serial);
if (ch == -1) break;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rx_fifo->buffer[rx_fifo->put_index] = ch;
rx_fifo->put_index += 1;
if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0;
/* if the next position is read index, discard this 'read char' */
if (rx_fifo->put_index == rx_fifo->get_index)
{
rx_fifo->get_index += 1;
rx_fifo->is_full = RT_TRUE;
if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/* invoke callback */
if (serial->parent.rx_indicate != RT_NULL)
{
rt_size_t rx_length;
/* get rx length */
level = rt_hw_interrupt_disable();
rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index):
(serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index));
rt_hw_interrupt_enable(level);
if (rx_length)
{
serial->parent.rx_indicate(&serial->parent, rx_length);
}
}
break;
}
其中ch = serial->ops->getc(serial);
可以接收到数据(实质数据保存在了一个ringbuffer里面),但 rx_fifo->put_index += 1;
执行后并没有变化,rx_fifo
指向地址0,初步怀疑是rx_fifo没有分配空间,对比串口1的程序,发现在执行static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
的时候,仅当模式为RT_DEVICE_FLAG_DMA_RX和RT_DEVICE_FLAG_INT_RX的时候,会执行:
rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
serial->config.bufsz);
否则会继续执行 serial->serial_rx = RT_NULL;
,所以把main函数中的rt_device_open(dev, RT_DEVICE_FLAG_RDWR);
改为rt_device_open(dev, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
就可以实现USB串口的收发了~