本文首发于稀土掘金。该平台的作者 逐光而行 也是本人。
前言
这几天以来用一些零碎的时间来复习嵌入式,也突然对嵌入式这个方向有了一些新的体悟。
我认为,抛开系统架构级别的不谈(我不了解的东西就不乱说了),如果是应用开发人员,应该要做到如下两点:
众所周知,嵌入式的应用级编程分为面向库函数编程和面向寄存器编程两种。
- 对于库函数编程人员,应该做到:给你一些需求,即使给你一块你从来都没有用过甚至见过的芯片,你也能照着文档、示例代码等依葫芦画瓢弄出来。
- 对于寄存器编程人员,给你一块你从来都没有用过甚至见过的芯片,扔给你一本命令手册,给你一个下午的时间,你也能照着手册上面某个寄存器各个位表示什么,把一些简单的函数以位运算赋值的形式写出来。
因此,你曾经学过什么并不重要,你能不能学完也不重要,重要的是有没有逐渐具备触类旁通和知识迁移的能力。不过这确实需要具备比较扎实的硬件基础知识,否则如果连最基本的知识都没有,迁移也无从谈起。
题目
假设嵌入式系统使用串口UART2进行数据收发,数据格式为9位数据,使用奇校验,2位停止位,串口波特率为38400,PCLK1频率36MHz。接收数据使用串口接收中断完成。请使用寄存器方式编写串口2的初始化函数UART2Init(),给出波特率分频值计算过程。
思路
为了做出这题以及理解这一块知识,我还特意去官网翻了手册,绝对保真。
代码及解析
void UART2Init(){
RCC->APB2ENR|=(1uL<<2);//打开PA时钟源,见下图1
RCC->APB1ENR|=(1uL<<17);//打开USART2时钟源,见图2
GPIOA->CRL &=~(((7uL<<4)|(1uL<<2))<<8);//配置GPIOA_CRL的第[15:8]位为10001011b,见图3
//也可以写成GPIOA->CRL|=(((1uL<<7)|(1uL<<3)|(3uL<<0))<<8);
RCC->APB1RSTR|=(1uL<<17);//复位USART2
RCC->APB1RSTR&=~(1uL<<17);//退出复位,进入工作状态
USART2->BRR=(58uL<<4)|(10uL<<0);//波特率38400对应的USART_DIV位,详细计算方法见点4
USART2->CR1 |= (1uL<<12); //M位,置1表示9位数据位,见图5
USART2->CR2 &= (2uL<<12);//两位停止位
USART2->CR1=(1uL<<13)|(1uL<<5)|(3uL<<9)|(1uL<<2);
//从左到右功能依次为:开启USART,开启RXNE,有校验且为奇校验;接收有效位
NVIC_EnableIRQ(USART2_IRQn);
}
- 图1:
- 图2:
- 图3:
这里指的是配置PA2和PA3两个口的参数。
以下是单个GPIO口的配置模式及约定。
- 点4:关于USART_DIV位的详细计算方法
有以下两条公式:
$$ USART\_DIV=FCK/(波特率X16) \\ USART\_DIV=整数部分+小数部分/16 $$
- 图5: