创客百科

姿势共享,有节操无门槛参与的创客百科,创客动力之源 \ (^_^) /

用户工具

站点工具


arduino:cores:cdc.cpp
/* 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) */
本页面的其他翻译:
arduino/cores/cdc.cpp.txt · 最后更改: 2016/12/25 22:15 (外部编辑)