本帖最后由 wziyi 于 2021-5-24 22:33 编辑
与 AVR 的第一次相遇(三)这节要不来个串口玩玩?按以往惯例,该讲解定时器的,主要是我想试试软件仿真里的串口该怎么玩。串口是嵌入式硬件里最简单、最常见和最重要的串行通信协议。它的同类有 I2C , SPI , CAN,网口和 USB 等等。它们都是串行通信口,有的属于对等,有的属于非对等,都可以由多个节点组成网络。单片机一般可以分为内核和外设两大部分,外设里又可以把串行通信外设作为一个大类来讲。以串口的学习为例,可以分为协议层和硬件层。硬件层里有 TTL 电平,RS232 电平和 RS422/RS485 电平。主要用在 PCB 板上通信,对标的是 I2C 和 SPI。RS232 电平和 RS422/RS485 电平用在远距离通信,对标的是 USB 和 CAN。按通信协议分为同步和异步,常见的是异步,同步的我也没用过。通信特性还可分为波特率,数据位,停止位,校验位和流控制位。 下面便是直接与芯片本身相关的知识了,首先从特性中获取主要信息。半双工模式里有个单线模式,这个在 PIC 里就没有,目前我只在 ST8 和 STM32 见过。这个很有用,在很多工业应用环境下,多一条线会对工艺提出很高的要求。剩下的部分都是常规信息了,与同类芯片都是一样的。下图中有个小数波特率发生器要关注下,它可以能够基于任何系统时钟频率生成各种 USART 波特率。在很多低端单片机就没有这个东西,所以有时候为了得到合适 USART 波特率需要设置特定的系统时钟。
我初学单片机的时候有一个超级大坏习惯就是不爱看图表,数字芯片以逻辑流程图为主而模拟芯片则以参数指标图为主。从下面流程图可以看出它各个寄存器间的联系,也可以大致知道哪些寄存器是重要配置项,比如 TXDATA 和 RXDATA 就是存放数据的地方。 接下来是串行通信协议的时序图了,是每种单片机芯片都会有图表。在里面必须的搞懂一个很重的概念码元和数据帧,编码与解码。这属于通信原理里面东西,可以自己看相关网课。下面这个时序图表示就是它的数据帧是如何构成的。最开始的是起始位(火车头),然后是数据位位(乘客),随后是停止位(火车尾),最后的校验位是选配的。 紧接着便是介绍如何计算合适波特率的公式。其中寄存器的高 10 位保存整数部分,低 6 位保存小数部分。 发现了个新鲜玩意,这个 UART 貌似可以直接复用为 SPI 。这是我第一次见,还没搞清楚咋回事,先在这提一下。 一直在啰嗦软件的东西,有点腻味了,来点硬的。芯片大厂的原理图看起来就是那么赏心悦目,今天给朋友讲解复旦微单片机的时候,文档看的想吐,且不说各种错别字,东西还写的乱七八糟缺胳膊少腿的。新人啊,千万不要去碰国产单片机,没两把刷子,容易崩着牙。下面这幅图画得真的好美,把总线画成萌萌的螺旋线,外设排列的整齐画一宛若棋盘。隔壁 NXP 和 ST 的单片机开发板原理图好像大多是很传统的那种层次图。以后要把这种画法全面用在自己设计中。 写个小插曲,今天在给小师弟讲代码的时候翻车了,要对寄存器的某一个位取反,头脑第一个想到的位运算符 ^ ,就卡这了,不知道怎么写了。吃完晚饭,看看 C 语言手册,看来代码必须天天写,不然会快速倒退的。 PORTC.OUT ^= (1<<3) //PORTC.OUT 寄存器的第 3 位取反
特意找了 ST 的牛可乐板,虽然也很整齐美观,与 AVR 相比,还是差那么点点。NXP 的电路原理图是 orCAD 格式的,电脑上没装 Cadence 暂时不好展示。 从原理图看出外部晶体振荡器有 16MHz 和 32.768KHz 两个,带有一个灯 PB3 为低时亮。有一个按键却没有去耦电容,这意味着按键不能使用中断只能用轮询。要是使用中断,按键抖动会导致单片机反复进入中断易造成堆栈溢出。 接着发现有两个串口分别与调试器相连,其中 USART3 与 CDC 相连。这里解释下,现在调试器往往会集成串口通信的功能,它是利用 USB 通讯设备类(CDC)来与电脑进行数据交互的。这个开发板就是个高配的最小系统板,电路部分也就这么点点。这里主要想科普下关于原理图的基础知识,那根比较粗的线叫做总线。就是把好几条线放在一块,普通电线不能直接和它连接,得先给总线添加电气连接线,就是那些直接与总线相连的 45 度斜线。那些黄色的长方块是端口,名字一样的端口是具有电气连接的。
打工人太累了,五月加班多没时间搞,等这么久才有机会写代码。就先用串口不停地发送字节吧,一般通信里面发送都比接收简单的多。串口接收数据用软件仿真还不太好实现,等以后在说。 - #include <avr/io.h>
- #include <avr/cpufunc.h>
- #include <avr/delay.h>
- FUSES = {
- .WDTCFG = 0x00, // WDTCFG {PERIOD=OFF, WINDOW=OFF}
- .BODCFG = 0x00, // BODCFG {SLEEP=DISABLE, ACTIVE=DISABLE, SAMPFREQ=128Hz, LVL=BODLEVEL0}
- .OSCCFG = 0xF8, // OSCCFG {CLKSEL=OSCHF}
- .SYSCFG0 = 0xDA, // SYSCFG0 {EESAVE=CLEAR, RSTPINCFG=RST, CRCSEL=CRC16, CRCSRC=NOCRC}
- .SYSCFG1 = 0xE8, // SYSCFG1 {SUT=0MS, MVSYSCFG=DUAL}
- .CODESIZE = 0x00, // CODESIZE {CODESIZE=User range: 0x0 - 0xFF}
- .BOOTSIZE = 0x00, // BOOTSIZE {BOOTSIZE=User range: 0x0 - 0xFF}
- };
- LOCKBITS = 0x5CC5C55C; // {KEY=NOLOCK}
- #define USART1_BAUD_RATE(BAUD_RATE) (((float)3333333 * 64 / (16 * (float)BAUD_RATE)) + 0.5)
- void system_init(void);
- void uart_init(void);
- int main(void)
- {
- unsigned char a=0;
-
- system_init(); //系统初始化
- uart_init(); //串口初始化
- while(1)
- {
- _delay_ms(100); //适当延时方便后续观察
- while (!(USART1.STATUS & USART_DREIF_bm)); //等待串口发送就绪
- USART1.TXDATAL = a; //使串口不断发送 0 到 256 之间的数字
- _delay_ms(100);
- a++; //数字自加
- }
- return 0;
- }
- void system_init(void){
- CLKCTRL.MCLKCTRLA = 0x80; //内部高速振荡器,时钟在对外输出
- CLKCTRL.MCLKCTRLB = 0x00; //关闭预分频器分频
- CLKCTRL.OSCHFCTRLA = 0xbc; //24MHz,一直开启,关闭时钟修正
- }
- void uart_init(void)
- {
- USART1.BAUD = (uint16_t)USART1_BAUD_RATE(9600); //设置波特率
- USART1.CTRLA = 0x00; //关闭中断
- USART1.CTRLB = 0x40; //打开发送功能
- USART1.CTRLC = 0x03; //配置异步-校验位-停止位-数据位
- USART1.DBGCTRL = 0x00; //关闭调试模式
- USART1.EVCTRL = 0x00; //关闭 IrDA
- USART1.RXPLCTRL = 0x00; //关闭 RXPLCTRL_RXPL
- USART1.TXPLCTRL = 0x00; //关闭 TXPLCTRL_TXPL
- }
从图中可以看出控制台输出了很多奇奇怪怪的字符,代码中是输出 0 到 256 的十六进制数,终端仿真显示的是十六进制数所对应的字符码。这是 C 语言里面的知识,自己可以翻翻书。
|