Arduino 101 Serial 实现详解

Arduino 101 连接 PC 是通过一条方口 USB 数据线,并且可以通过这条数据线接收板子输出 debug 调试。
那么这是如何实现的呢?
这部分没有完全看透,只记录下目前收获(也许有误)。

概述

Arduino 中操作串口是通过 Serial1 类。

初始化

1
2
3
4
void setup() {
Serial.begin(9600); // 波特率设置为 9600
while(!Serial); // 等待串口初始化完成
}

发送

1
2
3
void loop() {
Serial.print("Hello World!\n");
}

接收

1
2
3
void serialEvent(){ // Called when data is available. Use Serial.read() to capture this data.
Serial.print(Serial.read());
}

分析

查看代码时发现了应用程序入口其实在 main.cpp 中,如下:

C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main( void )
{
initVariant();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) /* This infinite loop is intentional and requested by design */
{
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}

从中可以看到 setup loop 等函数,这就是通用的 Arduino 代码函数块,可以发现 loopserialEvent 其实是顺序轮询方式,并不是真正中断。

初始化

Serial 部分的源码又在哪里呢?查找后发现对象初始化部分:

C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\variants\arduino_101\variant.cpp

1
2
3
4
5
6
7
uart_init_info info_cdc;
CDCSerialClass Serial(&info_cdc);

RingBuffer rx_buffer_uart;
RingBuffer tx_buffer_uart;
uart_init_info info_uart;
UARTClass Serial1(&info_uart, &rx_buffer_uart, &tx_buffer_uart);

原来 Serial 是全局变量,CDCSerialClass 的构造函数只是赋值,并没有特殊操作。
现在已知如下(和板子定义一致):

  • Serial 表示 USB2 串口
  • Serial1 表示 UART 串口

接下来的操作在 initVariant() 中:

1
2
3
4
5
void initVariant( void ) {
/* Initialise CDC-ACM shared buffers pointers, provided by LMT */
Serial.setSharedData(shared_data->cdc_acm_buffers);
...
}

Serial.setSharedData 只是赋值串口对象指针。

C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\CDCSerialClass.cpp

1
2
3
4
5
6
void CDCSerialClass::setSharedData(struct cdc_acm_shared_data *cdc_acm_shared_data)
{
this->_shared_data = cdc_acm_shared_data;
this->_rx_buffer = cdc_acm_shared_data->rx_buffer;
this->_tx_buffer = cdc_acm_shared_data->tx_buffer;
}

其中 shared_data 是芯片中的 SRAM3 地址为 0xA8000000,共 80K4

sram

关于这段内存的作用,我推测是和内核部分进行交互,因为 CDCSerialClass 中并没有对寄存器的操作。
而内核部分也有同样的结构,并且会读取标志位以及设置标志位(内核部分没有详细查看)。
内核相关理解如下:

  • 代码入口在 CODK\CODK-A\x86\projects\arduino101\quark\main.c
  • 驱动部分代码 CODK\CODK-A\x86\bsp\src\drivers\usbusb_driver_intf 没有找到声明地方)

其他

Serial.print 实现为例,使用该函数会向串口发送字符串。

CDCSerialClass 中并没有定义 print 的方法,此方法乃是继承 class Print,其所有继承关系如下:

CDCSerialClass->HardwareSerial->Stream->Print

某个方法的实现如下:

C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\Print.cpp

1
2
3
4
size_t Print::print(char c)
{
return write(c);
}

write 方法在 CDCSerialClass 类中被重写为向串口发送数据,如此便实现了 Serial.print 的功能。

疑问

CDCSerialClass 中波特率设置似乎没有作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
void CDCSerialClass::init(const uint32_t dwBaudRate, const uint8_t modeReg)
{
/* Set a per-byte write delay approximately equal to the time it would
* take to clock out a byte on a standard UART at this baud rate */
_writeDelayUsec = 8000000 / dwBaudRate;

/* Make sure both ring buffers are initialized back to empty.
* Empty the Rx buffer but don't touch Tx buffer: it is drained by the
* LMT one way or another */
_rx_buffer->tail = _rx_buffer->head;

_shared_data->device_open = true;
}

以上为初始化部分,从中可以看到 dwBaudRate 只计算了 _writeDelayUsec,没有传递到 _shared_data 中。
查找代码又会发现 _writeDelayUsec 没有在其他地方被调用过,那么该波特率还有作用吗?
为此我尝试在电脑上随便修改波特率,并和板子通信,发现波特率不匹配也可以通信,不知道是不是因为该口是 USB 虚拟串口的原因?
相对应的 Serial1 UARTClassinit 中是包含波特率设置代码的。

修改

添加 printf 方法5
我使用的是添加 Print::printf,修改后可在 Serial 中使用 printf 方法了。

C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\Print.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdarg.h>
#define PRINTF_BUF 80
size_t Print::printf(const char *format, ...)
{
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
for(char *p = &buf[0]; *p; p++) {
if(*p == '\n')
write('\r');
write(*p);
}
va_end(ap);
}

  1. 1.Serial func
  2. 2.USB CDC is a composite Universal Serial Bus device class
  3. 3.static random access memory
  4. 4.Intel® Quark™ SE Microcontroller C1000 Datasheet, Table 24, Mapping Address Spaces
  5. 5.Arduino printf

Arduino 101 Serial 实现详解
https://wishlily.github.io/article/arduino/2017/07/03/undefined/
作者
Wishlily
发布于
2017年7月3日
许可协议