嵌入式控制器基础知识普及

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

嵌入式控制器基础知识普及
Version Author Date Modify V1.0 SixRoom 2011/3/261创建文件
2
编写基本知识点
V1.1 SixRoom 2011/7/12 1整理文档,修改模块顺序
一、存储器映射基础知识
1.1 存储器简介
编译好的程序一般需要保存在存储器中,这样芯片内部的处理核可以通过地址寻址读取存储器中指令。

由此可以看出,这里所使用的存储器必须是可以直接寻址的,否则无法读取想要的指令。

嵌入式系统中常用的存储器分为两大类:RAM、ROM,这里只提供简化图表:
RAM 存储器静态RAM (SRAM):一般内置于芯片内,如ESRAM
常用作二级Cache
动态RAM (DRAM):SDRAM、DDR SDRAM
使用前要先初始化
NorFlash:易读不易写,可执行代码(XIP)
NandFlash:不易读写,一般仅用于存储
ROM (Flash)
具体选择哪种存储器需要综合考虑用途、容量、成本、速度等方面因素。

低端芯片内部RAM一般是SRAM,而且内部Flash一般具有NorFlash的特性,也即可以直接寻址读,但写需要一定的时序才可实现,于是代码一般存放在内部Flash中(可以直接在Flash执行代码,也就是说只要可以直接寻址读,就可以在这个存储器中执行程序),而在程序中定义的变量等需要不断读写的数据存放在内部RAM中。

1.2 存储器映射
1.2.1 寻址范围
存储器的访问是通过地址/数据总线寻址来实现的,地址线的个数决定了寻址范围,对于8位的单片机而言,地址总线一般为16位,寻址范围为2^16个单元(每个单元是字还是
字节也要针对具体芯片来定的,比如Atmega128的每个单元为16位的半字,地址总线为16位,那么它的最大寻址能力为2^16*2字节,也就是128Kbyte),32位的ARM一般为32位总线,每个单元都固定为1字节,那么最大寻址能力为2^32字节。

1.2.2 编址方式
主存储器空间分为数据存储器和程序存储器。

对于ARM芯片来说,这两种存储器统一编址,也即在同一寻址空间中,如右图所示。

ARM芯片的存储器读写的基本原理依然是通过地址总线寻址,
然后通过数据总线传输待读写数据,但芯片内部都有一个专门的总线
控制器,比较常用的有AMBA总线和AXI总线,主要是起一些管理
和控制传输的功能,ARM内核将要访问的逻辑地址交给总线控制器,
然后由总线控制器将其翻译成物理地址去访问,也就是说只要在设计
芯片时修改总线控制器的“逻辑<->物理地址对应”表,就可以很方
便修改逻辑地址,比如Timer模块的寄存器地址,但是要保证两个地
址不能重合。

下文将要讲解的地址重映射功能也是有总线控制器实现
的。

注意,这里很容易和MMU的“虚拟地址<->物理地址”的概念
混淆,MMU是ARM内核的一部分,而总线控制器是片内外设,也
即MMU仅可以访问总线控制器中的逻辑地址,因此MMU所指的物
理地址其实是总线控制器中所谓的逻辑地址。

AMAB总线非常复杂,除了地址变换,它还可以控制传输的数
据大小,以及传输数据的顺序等。

而对8位单片机来说,很少提及总线控制器的概念,但芯片内部
也是有这么一个外设功能模块,一般由芯片设计商自己根据需求设计
此控制器模块,而不像ARM芯片采用标准的AMBA/AXI总线,因
此在数据手册中不进行提及。

因为8位单片机的逻辑寻址空间很小,所以很少见到统一编址
的。

一般采用数据存储器、程序存储器独立编址,也即即使访问同一
地址,如果访问存储器类型不一样,那么得到的数据也是不一样的,
参见“1.4 Atmega128存储器映射”。

1.3 地址重映射
高端芯片,比如一些可以运行Linux操作系统的芯片,在存储器管理部分都还会有一个概念:地址重映射。

顾名思义,就是为一个实际地址再分配一个虚拟(映射)地址,通过总线访问虚拟地址时和直接访问实际地址是一样效果。

可以将这个虚拟地址看作实际地址的“绰号”。

举例,基于ARM7 720t内核的SEP4020,内部有一个64K的ESRAM,首地址为0x04000000,并且外部扩展了一个64M的SDRAM,首地址为0x30000000。

这个芯片在上电启动流程可以参考《MCU编译和运行浅谈》,这里只讲使用JTAG加载代码的过程:因为ESRAM不需要初始化就可以直接使用,而且速度比较快,我们希望我们的代码可以在ESRAM中运行,但如果跑Linux系统,则代码大小会达到M级,ESRAM的已经装不下这
么大的代码了,所以一般的方法是在ESRAM中存放、运行启动代码,在SDRAM中存放、运行系统代码。

这里有三点疑问:
1、为什么不全在SDRAM中跑?
之前也说了,SDRAM使用前需要初始化,因为启动代码必须要放在ESRAM中运行,而且启动代码就包括初始化SDRAM的代码,也即芯片上电后需要先在ESRAM中运行用于初始化硬件的启动代码,初始化SDRAM后再开始在SDRAM中运行代码。

2、地址重映射的作用在哪?
我们知道异常、中断向量表一般在0地址,当产生某个异常时PC指针会自动跳转到相应向量处执行异常处理程序。

但SDRAM并不是在零地址,系统代码又是在SDRAM中,因此一般做法是将异常向量表放在SDRAM的开始处,然后将SDRAM重映射为0地址,那么当产生异常时就可以顺利执行异常处理程序了。

3、为什么没有一个存储器的首地址为零地址?
如果有一个存储器的物理地址为0地址,那么当SDRAM重映射为0地址时就会和此存储器重合,这是不允许的,因此0地址附近都会留出足够的空间用于映射任何一种RAM,比如SEP4020的SDRAM是64M,也即0x04000000,而ESRAM的首地址为0x04000000,恰好没有重合,也就是说SEP4020最大只能扩展64M的SDRAM。

地址重映射概念只会在高端ARM芯片中出现,之前也说了,是因为内部存储器不够用了,才会去扩展容量比较大的外部存储器,而且又要保证在外部存储器中能顺利执行中断和异常向量,所以才有地址重映射概念,低端单片机一般没有这个功能,这里仅仅做一些知识普及。

1.4 Atmega128
1.4.1 存储器映射
A VR编译好的代码一般在内部Flash中(程序存储器)运行,此处的Flash可以很方便的进行地址寻址读,但写Flash需要一定的操作,比较麻烦。

程序存储器共有128K,使用64K*16的组织形式,也就是指每个地址存放一个16bit的数据。

分为两个部分:程序段、启动段。

这里还要注意,Flash的寻址空间和RAM的寻址空间是独立的,也即RAM也有一个从0x0000~0xFFFF的地址空间。

从下图中可以看出,所有片内外设的寄存器都是分配在RAM 地址空间中的,也即只要访问内部RAM中的对应地址就可以读写寄存器了,而且内部SRAM
为4Kbyte,外部最大可扩展SRAM为64K:
1.4.2 向量地址
程序运行过程中,当产生复位或者中断事件时,PC指针会自动跳转到对应的向量地址去运行,Atmega128的向量地址可以通过熔丝位BOOTRST和IVSEL来配置复位向量和中断向量的地址(这里的Boot区就是指上文提到的启动段):
中断向量有31个,中断向量表的分配参见“2.4Atmega128向量地址”。

注意每个向量大小为2字节,因此中断向量起始地址都会绕过复位向量从0x002开始放起。

举例来说,假如配置熔丝位为00,那么芯片会认为程序存储器中的Boot段的首地址为复位向量,也就是当产生复位异常时PC指针自动跳转到Boot段的首地址来执行;当产生INT1中断,通过查阅中断向量表可知INT1是第二号中断,也就是“中断向量起始地址+0x02”,因为中断向量起始地址是程序存储器的0x0002地址,那么INT1的中断向量的方向为0x0004地址,也即PC指针会跳转到0x0004地址运行。

二、VIC基础知识
2.1 简介
VIC(向量中断控制器)应该说是MCU中最核心的功能模块之一,MCU上电运行后是全速运行的,也就是此时MCU处理核的占用率是100%,但是假如当它在执行一个任务时用户想通过按键来与MCU交互,就必须中止当前的任务去处理按键,此时就是通过中断来实现的。

VIC负责捕捉所有中断源所产生的中断事件,并通知MCU处理核。

中断,顾名思义,就是中止当前任务,去响应突发事件。

这里还有个误区:就是“异常”,其实它和“中断”是包含关系,异常包括复位异常(当按下复位按键时产生)、指令异常(当出现未知的指令时)、中断等,也就是说中断是一种特殊的异常。

高端ARM中除了有中断向量表还有一个异常向量表,当产生中断时首先跳转到异常向量表找到中断,然后根据此处的值跳转到中断向量表中去执行中断服务子程序。

当然,很多低端单片机中并不区分异常,Atmega128中就没有异常概念。

2.2 向量及中断流程
“向量”一般人都不陌生,它是一个有方向的值。

在MCU中向量的“方向”可以理解为一个地址指针,指向某个固定地址,向量的“值”就是在这个固定地址里的代码。

也就说如果MCU内部有VIC(向量中断控制器),当产生某个中断时,PC指针会自动的通过向量中断找到中断入口。

以Atmega128为例,每个中断和复位在程序空间都有一个独立的中断向量:
;ATmega128 典型的复位和中断设置如下:
;内存地址 指令 注释
$0000 jmp RESET ; 复位句柄
$0002 jmp EXT_INT0 ; IRQ0 句柄
$0004 jmp EXT_INT1 ; IRQ1 句柄
$0006 jmp EXT_INT2 ; IRQ2 句柄
$0008 jmp EXT_INT3 ; IRQ3 句柄
... ... ... ...
$0046 RESET:ldir16, high(RAMEND); 主程序
$0047 out SPH,r16; 设置堆栈指针为RAM 的顶部
$0048 ldi r16, low(RAMEND)
$0049 out SPL,r16
$004A sei ; 使能中断
$004B <instr> xxx
... ... ... ...
以上这段代码是放在内存0地址的启动代码,可以看到0地址是复位异常向量,此向量的“方向”是0x00地址,“值”是“jmp RESET”指令,也就是说当产生复位条件时,MCU 停止当前任务,PC指针自动跳转到0x00地址去运行,此时执行的就是“jmp RESET”指令,也就是跳转到标号“RESET”去,也就是代码中红色标注处。

同样道理,当产生IRQ2中断时,MCU停止当前任务,并强制PC指针跳转到0x06处执行“jmp EXT_INT2”,也就是跳转到IRQ2的中断处理程序“EXT_INT2”中运行(在汇编代码中,标号即是函数名;汇编代码也可以外部调用C代码中的函数)。

那么为什么中断向量的“值”仅仅是个跳转语句呢,为什么不在中断向量的“值”里放中断处理函数,而是将处理函数放在其它地方,然后通过跳转的方法去执行呢?
这很容易想到,中断处理函数中的代码数会根据待处理的内容或者编程人员的思路有很大不同,也就说有可能处理函数需要18行代码,也有可能需要58行代码,如果把处理函数就放在中断向量的“值”里,那么其它向量的“方向”就会是个根据代码而不断改变的地址,毕竟要避免向量空间的重合。

于是就有了一个标准:向量的值里放置一个大小固定的跳转语句,比如上面的代码中每条跳转语句都固定占用2字节空间。

这样的话,因为处理函数在和其它代码放在一起,可以通过跳转语句找到,所以根本不会影响向量“方向”的制定,而且处理函数的代码大小不再受限制。

这种方法在ARM中也延续下来,包括异常向量表和中断向量表都是仅在向量的“值”中存放跳转指令,不过ARM的跳转指令需要4字节,因此每个向量间也间隔4字节。

2.3 中断配置
前面也说过“VIC 负责捕捉所有中断源所产生的中断事件,并通知MCU 处理核”,同时VIC 还提供中断“总开关”以及“各个中断源开关”,也就说即使有中断事件,但也必须保证全局中断使能以及对应的中断源使能或者没有被屏蔽,此时才会产生中断信号,才会通知MCU 有中断产生。

通知MCU 处理核有中断到来 。

还有一个中断优先级问题,当两个中断源同时有中断产生时,会根据它们的优先级来选择执行优先级较高的中断。

一般有一个默认的优先级,也可以通过配置相关寄存器来提高或者降低某个中断源的优先级。

2.4 Atmega128向量地址
A VR 编译好的代码一般在内部Flash 中(程序存储器)运行,此处的Flash 可以很方便的进行地址寻址读,但写Flash 需要一定的时序,与NorFlash 特性类似。

下图所示的程序地址仅仅是当熔丝位BOOTRST 和IVSEL 为10时的情况,假如熔丝位改为00,那么中断向量的程序地址不变,但复位向量的程序地址应该为Boot 段的0地址了。

因此下图要灵活使用。

还有,向量所在的地址越低,优先级越高。

RESET 具有最高的优先级,下一个则为INT0--外部中断请求0。

优先级在中断嵌套中非常有用。

三、GPIO基础知识
3.1 简介
GPIO(general purpose input output)通用输入输出端口,也即具有的读--修改--写功能的IO口。

但要注意,MCU芯片的管脚并不都是GPIO,如下图Atmega128芯片所示,除PA/PB/PC/PD/PE/PF外,其余的都不是GPIO,比如21脚VCC和24脚XTAL1就是专用管
脚,它们没有所谓的读、写功能,也即不能通过软件进行编程。

如上图所示,Atmega128共有64个管脚,其中有53个可编程IO口。

3.2 GPIO复用
随着MCU芯片集成度的大幅提升,芯片内的功能模块也越来越多,很多功能模块需要和外设进行通信,这样就需要大量的GPIO,但很明显GPIO的数目直接影响芯片表面积,高端的芯片比如S3C6410开始采用BGA封装来兼顾管脚数目和芯片尺寸,但仅靠减小管脚间距并不能解决GPIO需求量,芯片设计人员开始使用“管脚复用”来缩减GPIO需求量。

“管脚复用”均是针对GPIO而言,具有“管脚复用”功能的GPIO通常都对应一个寄存器,可以通过为这个寄存器配不同的值来选择此GPIO当前的功能。

理论而言,每个GPIO 的复用量是没有限制的,但如果一个GPIO复用功能过多,就会导致芯片功能不灵活,比如有几个GPIO被复用为SDIO接口、AD的采样接口和LCD的数据接口,那么除非使用分时复用的方法去操作这几个GPIO(会大大增加软件复杂度,影响整个系统稳定性),否则没有办法做到“实时采集数据保存到SD卡并显示在液晶屏上”这个功能。

因此对于MCU这种内部功能模块相对较少的芯片来说,一般IO口被复用两次,也就是第一功能是通用IO,第二功能是专用IO,也就是直接连接到内部功能模块的IO口。

对于高端的ARM芯片,比如S3C6410,有些GPIO会被复用6次之多。

3.3 GPIO基本配置
管脚复用:专用功能、通用功能
传输方向:输入、输出
外部中断:使能、禁能外部中断功能
3.4 外部中断
某些GPIO在选择通用用途、设置为输入并开启外部中断功能时,可以实时监测管脚上的电平,当出现合适的电平或者跳变时,会触发中断,产生中断事件。

常用的触发信号有:上升沿、下降沿、高电平、低电平。

外部中断比较常用于按键检测,如下图所示:
可以很容易分析出来,当K1没有按下时,EINT1处为高电平,按下时也就相当于接地了,为低电平。

也就是说在K1按下的一瞬间,EINT1经过了“高电平->下降沿->低电平”,那么如果配置为“下降沿触发中断”或者“低电平触发中断”,那么当按键按下时就会有中断触发事件。

四、TIMER基础知识
4.1 简介
Timer是单片机中的基本模块,很少见到不带Timer模块的MCU芯片,它不是通讯接口,与外设的交互不多,但在代码执行过程中起着至关重要的作用,有了它,编程人员可以做出很多有意思的设计,比如间隔循环执行代码、嵌入式操作系统时间戳以及模拟通讯时序时都会用到。

定时器,顾名思义,就是在芯片内部定时一定的时间,然后内部的计数器开始计数(计数器跟秒针差不多,只不过这里计数器计数一次并不是1s,而是可以自己设置一次计数时间),当计数到定时时间时会通知单片机,单片机得到“定时到”的信号后会进行一些操作,当然这里的操作可以指Timer硬件模块自身的行为,比如使用Timer相关的外部IO口产生PWM波,也可以指用户指定的行为,比如定时中断服务子程序。

4.2 基础参数
计数时钟:一般是系统时钟,或者从系统时钟分频得到
计数方向:增计数、减计数、先增后减计数
计数器位宽:8位、16位、32位
计数模式:OneShot、Restart、Free
有无外部输出管脚:如果有,则需要关注“匹配输出”:保持、翻转、置高、置低,一
般用于产生PWM信号。

定时器的位宽决定了一次可以定时的最大时间,假设当前计数时钟(从系统时钟分频而来)为1MHz,也即一次计数占用1us,那么32位的计数器计数一周为2^12秒,16位的计数器占用64毫秒,8位的计数器占用256微秒。

一般而言,MCU内部的定时器位宽和寄存器位宽相同(因为当前计数值要用寄存器来保存),也即和芯片位数相同,但具体使用芯片时要根据芯片的datasheet来判断,比如AT89C51是8位单片机,那么寄存器位宽肯定为8位,那么定时器位宽也为8位,但是可以通过配置相关寄存器使器使用两个寄存器来存放计数值,那么计数范围就为2^16了;再比如LPC2131(ARM7)芯片,定时器位宽为32位,但可以通过配置把一个32位的定时器拆成两个16位的定时器来用,因为很少用定时器定时到秒级,一般都是us或者ms级别的定时,此时使用32位的定时器会造成浪费,当然使用8位的定时器就会有定时时间短的问题,虽然可以使用“多定时器几次”来加大定时时间,但毕竟不如一次定时来的方便。

4.3 编程方法
4.3.1 时间片
定时器最大的用途在于产生“时间片”,也即设定一个定时值,当计数器计数到此值时产生定时事件,循环往复,此次定时事件到下次定时事件之间的这段时间就是一个时间片。

因为定时器的计数器是硬件模块,在计数器计数的同时单片机依然可以执行代码,也就说定时器和处理器内核之间的工作是并行的,定时器仅当产生定时事件时才会通过中断来通知处理器内核,达到交互目的。

因此我们可以在定时器中断服务子程序中设置一些标志位,然后在main函数中循环查看这些标志位,根据标志位的值执行不同操作,就达到分时复用的目的了。

下面的代码就是用于为几个任务分配时间片:首先将配置定时器为Restart模式,并设置定时时长为10ms,然后进入while循环执行默认任务(此处是根据gFlag的值来执行不同任务,也就是说我们只要通过修改gFlag就可达到执行不同任务的效果),同时等待中断触发(注意,此时是硬件在等待中断信号,也就说我们的代码仍然在执行着,这和软件查询等待中断标志位在效率上有很大区别),当10ms的定时事件到,进入中断服务子程序执行。

毫无疑问,中断服务子程序的作用就是修改gFlag的值了,而且在中断服务子程序中可以加入一些代码使得为每个任务分配不同的时间片个数,也就是说让某些任务执行的时间多点(TASKx_TICK),某些任务执行的时间少点,也可以修改每次任务循环周期的大小(TASK_END)。

而且如果想更复杂,可以在中断服务子程序以及main中的while循环中加入任务的优先级调度和启动/关闭任务的代码,这样就可以看作是一个轻量级的操作系统了。

….
TASK0 TASK1 TASK2TASK0TASK1TASK2….
0ms 10ms 20ms 30ms 40ms 50ms ms
真实的操作系统一般也是使用专门TICK定时器来生成时间戳的,也即之前所说的时间片。

在操作系统中当分配给某个进程的时间片结束时,它需要交出使用权,也就是把自己暂停,并将PC指针指向“调度程序”指定的那个进程,也即系统开始执行另一个进程(这个过程一般使用中断来实现)。

一般时间片的时间大小在ms左右,有些也用0.1ms,时间片太
小的话进程的频繁中断和跳转造成资源损耗(每次中断都需要保护、恢复现场,耗时约为us级),太大则会减弱调度的灵活性。

/*此处的宏定义用于确定每个任务在一次循环中所占用的时间片个数*/
#define TASK0_TICK10
#define TASK1_TICK20
#define TASK2_TICK30
#define TASK_END30
int gFlag=0;/*全局变量*/
void TIMER_IRQHandle(void)
{
static int i;
TIMER_ClrInt(TIMER_ID);
i++;
if(i == TASK0_TICK)
gFlag=0;
else if(i == TASK1_TICK)
gFlag=1;
else if(i == TASK2_TICK)
gFlag=2;
else if(i == TASK_END)
{
i=0;/*重置中断次数,以进入下一次任务循环*/
gFlag=0;/*默认回到第一个任务*/
}
}
int main(void)
{
TimerInit(RESTART, 10);/*Restart模式,定时10m s*/
while(1)
{
switch(gFlag)
{
case 0:
myTask0();
break;
case 1:
myTask1();
break;
...
...
}
}
}
4.3.2 精确延时
可以使用Oneshot模式定时一定的值,因为只计数一周,所以也仅会产生一次定时中断,这种功能在高端芯片中非常常用。

在使用流水线的芯片中,每条指令执行的时间因为流水线的优化导致无法估量,但有些外设,比如DS18B20(数字温度传感器)驱动时需要严格的时序,就不能使用“空延时”的方法,此时一般利用定时器的Oneshot模式进行延时,因为定时器的计数是硬件操作,跟流水线没有关系。

但一般作为此用途时要保证配置代码以及中断服务子程序非常简短,因为代码的执行也是需要时间的,也可以尝试使用汇编写。

五、UART基础知识
5.1 英文名
UART (Universal Asynchronous Receiver/Transmitter),有些教材里也称:USART (Universal Serial Asynchronous Receiver/Transmitter)。

UART 在摩托罗拉的系列微控制器中常被称为串行通信接口(SCI ),TI 的DSP 中的串口也常叫做这个名字,但一般为同步串口,而非异步;Microchip 微控制器中常称作通用串行异步收发器(USART )。

5.2 基础参数与时序
协议图
(仅有两根数据线,RXD:接收线,TXD:发送线)
基本参数
波特率(常用值:2400、4800、9600、38400、115200 bps)
数据位数(5-8位可选长度字符)
奇偶校验(偶校验,奇校验,不用奇偶校验,或固定校验位) 停止位(1,1.5,2位)
中止符,是指所有信号位都是零,持续时间必须大于数据位、开始位、停止位和奇
偶校验位之和
数据流控,此功能目前用的不多,一般在驱动GPRS 模块和Modem 时使用。

串口一般
通过起始位、结束位判别传输的开始和结束,只要符合串口协议的数据都会接收,
而如果启用了数据流控,比如CTS/RTS,则非得CTS 线上的电平为低电平才能接收
或者发送数据。

UART 电平 TTL 电平
CMOS 电平
RS232电平,可以使用SP3232等电平转换芯片转换TTL 电平为RS232 发送
时序图
(8位字符、无校验位、2位停止位)
将数据寄存器中的数据一位一位的移出送到TXD 线上,一般分
为MSB 和LSB 两种发送顺序,MSB 是指在串行发送一个字节数据时,
先发送此字节的高位,再发送低位,LSB 反之。

可以看出上图的是
LSB 顺序。

接收
时序图
(8位字符、无校验位、2位停止位)
TTL 转
RS232
单片机的端口电平一般为TTL/CMOS 电平,如果和PC 上的串口进行
通信,必须将TTL/CMOS 电平转换成RS232电平输出。

5.3 超级终端
常用的串口调试软件有以下三种,个人比较推崇第二种,性能比较稳定,而且功能也很强大,具有自动保存记录等多项实用功能:
1. 串口调试助手
2. SecureCRT
3. Windows 自带超级终端,可以通过“开始”-> “所有程序”->“附件”->“通
讯”->“超级终端”来新建或者打开一个超级中断。

特别强调,在使用笔记本进行调试开发板时,一般笔记本并没有RS232接口,这
时我们需要是使用RS232_USB 转换模块在笔记本USB 口上模拟出来一个RS232接口,这里推荐使用“帝特”牌的RS232_USB 转换模块,可以在“设备管理器”中查看此模块的驱动是否安装成功,如下图所示的Prolific USB-to-Serial Bridge(COM5)即是USB 模拟的RS232接口:
5.4 UART编程方法
5.4.1 串口控制台相关函数
串口可以说是嵌入式开发中最常用的接口,开发人员喜欢使用串口作为标准调试接口(不是说JTAG,而是指控制台接口,如图1),主要还是因为台式电脑上自带RS232接口,和开发板连接方便,便于调试;市面上的很多低速器件都使用串口作为基本的通信接口,最典型的当属GPS、GPRS。

图1 串口控制台
串口控制台函数
头部定义typedef char S8;
typedef unsigned char U8;
/*RP8主要修饰寄存器,必须要加volatile用于取消编译器对寄存器变量的优化*/
typedef volatile U8 *RP8;
typedef enum {UART_ID0=0, UART_ID1=1} UART_ID;。

相关文档
最新文档