到目前为止,咱们的开发板只能处理很小量的数据:读取几个引脚电平,输出几个LED,顶多用数码管显示一个两位数字。至于输入一个指令、输出一条调试信息,甚至用scanf
和printf
来输入输出,在已经接触过的这些器件上是不可思议的。而本讲“串口发送”与下一讲“串口接收”,将打开这一扇大门。html
本讲的主题是UART(Universal Asynchronous Receiver-Transmitter,通用异步收发器),俗称串口。实际上串口是串行接口的统称,在单片机领域一般指UART。“串行”的意思是每次传输一个bit,而一个字节的数据被拆成8个bit传输;相比之下并行总线能够一次传输一个或多个字节(这并不意味着并行总线必定优于串行总线)。编程
AVR单片机提供的硬件组件不是UART,而是USART(S表明Synchronous,同步的),相比UART额外支持同步通讯。所谓“同步”是指收发双方经过时钟同步,“异步”是指没有时钟来同步,但实际上双方仍是由一些特殊信号同步的。安全
数据在UART总线上以“帧(frame)”为单位发送,以下图所示,带有方括号的位是可选的。异步
一帧包含一个起始位、5~9个数据位(经常使用8位;不少设备不支持9位)、可选的一个校验位(偶校验或奇校验,即全部数据位与0或1的异或结果)与1或2个终止位。起始位与终止位统称为同步位,用于在异步总线上起到同步的做用,这样接收方才能知道一帧什么时候开始。函数
波特率的定义是信息在通讯信道上传输的速率。假如信号线上的波形容许1秒有9600个方框(方框表示高电平或低电平,实际电平是其中一个),那么波特率就是9600。经常使用的波特率有9600与115200(打开Serial Port Utility或相似软件,可选的波特率都是经常使用的)。ui
在开始通讯以前,收发双方必须约定好波特率与帧格式。uart_init
函数的配置是波特率38400,8数据位,偶校验,1中止位。相应地在电脑的串口调试软件中也要这样配置。编码
因为printf
是变参函数,不是很安全(若是格式串和参数对应错,程序可能直接跑飞),我倾向于使用类型安全的函数,即函数经过C的句法知道实参类型(写错就编译错误,而不是经过编译器没法检测的格式串)。不过,avr-gcc的<stdio.h>
中仍是提供了printf
等函数,你能够了解一下。3d
库中提供的发送函数都是同步阻塞的,即等待硬件组件把数据所有发送完,函数才返回。这里的“同步”与刚才的“异步总线”所指是不一样的。关于“同步”与“异步”、“阻塞”与“非阻塞”的概念,能够参考:怎样理解阻塞非阻塞与同步异步的区别?调试
不难计算,总线发送一个字节的时间是几千个CPU周期,CPU会浪费大量时间在无用的等待上。这个问题直到咱们讲到中断才会解决(也许我会把它封装起来放进库)。code
咱们来写一个用串口发送按键与拨动开关信息的程序。若是你会相关的C#编程,就可让电脑响应按键事件。
#include <ee1/delay.h> #include <ee1/button.h> #include <ee1/switch.h> #include <ee1/uart.h> int main(void) { button_init(PIN_6, PIN_7); switch_init(PIN_4, PIN_5); uart_init(UART_TX); uart_print_string("start\n"); while (1) { for (uint8_t i = 0; i != BUTTON_COUNT; ++i) if (button_pressed(i)) { uart_print_string("button "); uart_print_int(i); uart_print_string("\n"); } for (uint8_t i = 0; i != SWITCH_COUNT; ++i) if (switch_changed(i)) { uart_print_string("switch "); uart_print_int(i); uart_print_string(switch_status(i) ? " on\n" : " off\n"); } delay(1); } }
程序首先将UART初始化为发送模式(UART_TX
),而后打印"start"
。在间隔一毫秒的循环中(实际上串口发送的时间远长于一毫秒,由于是阻塞的),程序检测每个按键与开关的动做,若是有则发送相应数据。
用printf
一行就能解决的操做这里须要三行才能完成,这就是权衡吧。
基于uart_print_char
,实现my_print_int
函数,在串口上打印一个int
类型整数(在avr-gcc中,int
类型默认是16位宽度;注意负号和0;你能够了解一下itoa
,尽管它是非标准的)。
将旋转编码器的数据经过串口传输给电脑。将原始数据(pin_read
或rotary_status
)与处理后的数据(rotary_rotated
)一同打印,你能够更直观地感觉数据处理的过程。