/* Copyright (c) 2011, Peter Barrett ** **基于MIT许可证发布 **被授权人有权利使用、复制、修改、合并、出版发布、散布、再授权及贩售软件及软件的副本。 **被授权人可根据程序的需要修改许可协议为适当的内容。 **在软件和软件的所有副本中都必须包含版权声明和许可声明。 */ #include "Platform.h" #include "USBAPI.h" #include <avr/wdt.h> #if defined(USBCON) #ifdef CDC_ENABLED #if (RAMEND < 1000) #define SERIAL_BUFFER_SIZE 16 #else #define SERIAL_BUFFER_SIZE 64 #endif struct ring_buffer { unsigned char buffer[SERIAL_BUFFER_SIZE]; volatile int head; volatile int tail; }; ring_buffer cdc_rx_buffer = { { 0 }, 0, 0}; typedef struct { u32 dwDTERate; u8 bCharFormat; u8 bParityType; u8 bDataBits; u8 lineState; } LineInfo; static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; #define WEAK __attribute__ ((weak)) extern const CDCDescriptor _cdcInterface PROGMEM; const CDCDescriptor _cdcInterface = { D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1), // CDC通信接口 D_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), D_CDCCS(CDC_HEADER,0x10,0x01), // 头部 (1.10 bcd) D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // 设备处理呼叫管理 (not) D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // 支持SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE,CDC_DATA_INTERFACE), // 通信接口为主, 数据接口为从0 D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40), // CDC数据接口 D_INTERFACE(CDC_DATA_INTERFACE,2,CDC_DATA_INTERFACE_CLASS,0,0), D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,0x40,0), D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,0x40,0) }; int WEAK CDC_GetInterface(u8* interfaceNum) { interfaceNum[0] += 2; // 使用2 return USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface)); } bool WEAK CDC_Setup(Setup& setup) { u8 r = setup.bRequest; u8 requestType = setup.bmRequestType; if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType) { if (CDC_GET_LINE_CODING == r) { USB_SendControl(0,(void*)&_usbLineInfo,7); return true; } } if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) { if (CDC_SET_LINE_CODING == r) { USB_RecvControl((void*)&_usbLineInfo,7); return true; } if (CDC_SET_CONTROL_LINE_STATE == r) { _usbLineInfo.lineState = setup.wValueL; // 当1200速率的端口关闭时,触发bootloader的自复位。 // 这是启动看门狗的信号, // 需要一个较长的周期,以使它结束日常任务, // 比如在草稿结束之前服务终结点。 if (1200 == _usbLineInfo.dwDTERate) { // 检查DTR状态来决定主机端口是否开放(lineState的位0). if ((_usbLineInfo.lineState & 0x01) == 0) { *(uint16_t *)0x0800 = 0x7777; wdt_enable(WDTO_120MS); } else { // 当配置端口时,大多数操作系统会做一些中间步骤, // DTR会在稳定前触发不止一次。 // 为了避免误复位,如果DTR回到高电平, // 我们把看门狗设置成250ms并最终取消。 wdt_disable(); wdt_reset(); *(uint16_t *)0x0800 = 0x0; } } return true; } } return false; } int _serialPeek = -1; void Serial_::begin(uint16_t baud_count) { } void Serial_::end(void) { } void Serial_::accept(void) { ring_buffer *buffer = &cdc_rx_buffer; int i = (unsigned int)(buffer->head+1) % SERIAL_BUFFER_SIZE; // 如果我们要把收到的字符存储在这个位置,正好在尾部之前 // (意思是头部会推进到尾部的当前位置), // 将会使缓冲区溢出,所以我们不写这个字符或推进头部。 // 当我们有空间来存储一个字节 while (i != buffer->tail) { int c = USB_Recv(CDC_RX); if (c == -1) break; // 没有更多数据 buffer->buffer[buffer->head] = c; buffer->head = i; i = (unsigned int)(buffer->head+1) % SERIAL_BUFFER_SIZE; } } int Serial_::available(void) { ring_buffer *buffer = &cdc_rx_buffer; return (unsigned int)(SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % SERIAL_BUFFER_SIZE; } int Serial_::peek(void) { ring_buffer *buffer = &cdc_rx_buffer; if (buffer->head == buffer->tail) { return -1; } else { return buffer->buffer[buffer->tail]; } } int Serial_::read(void) { ring_buffer *buffer = &cdc_rx_buffer; // 如果头部不是在尾部之前,我们没有任何字符(即头尾指针指向同一位置) if (buffer->head == buffer->tail) { return -1; } else { unsigned char c = buffer->buffer[buffer->tail]; buffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE; return c; } } void Serial_::flush(void) { USB_Flush(CDC_TX); } size_t Serial_::write(uint8_t c) { /* 仅在高电平的CDC连接本身就打开(不仅仅是管道)时,才尝试发送字节。 端口打开时,操作系统应当设置lineState;当端口关闭时清除。 在用户打开连接之前或关闭连接之后发送的字节会丢失,就像使用UART。*/ // TODO - ZE - 检查在不同操作系统下的行为并测试当打开的连接不是干净地断开 // (比如线缆被拽出,主机死机或锁定,主机虚拟端口挂起)时,会发生什么。 if (_usbLineInfo.lineState > 0) { int r = USB_Send(CDC_TX,&c,1); if (r > 0) { return r; } else { setWriteError(); return 0; } } setWriteError(); return 0; } // 这个操作符是草稿用来检查是否端口已被主机配置和打开(相对于只是被连接到主机) // 的一种便捷方法。比如说,打印前在setup()中, // 它可以用来确保主机上的应用程序已经确实准备好接收和显示数据。 // 我们在返回前加入了一个短暂延迟来修复Federico观察到的一个bug—— // 当端口被配置为(lineState != 0)但没有确实打开。 Serial_::operator bool() { bool result = false; if (_usbLineInfo.lineState > 0) result = true; delay(10); return result; } Serial_ Serial; #endif #endif /* if defined(USBCON) */