滴答计时器.doc_第1页
滴答计时器.doc_第2页
滴答计时器.doc_第3页
滴答计时器.doc_第4页
滴答计时器.doc_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1.systick介绍 Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了 给操作系统提供一个硬件上的中断(号称滴答中断)。没有学过操作系统的同学, 可能会很郁闷,啥叫滴答中断?这里来简单地解释一下。操作系统进行运转的时 候,也会有“心跳”。它会根据“心跳”的节拍来工作,把整个时间段分成很多 小小的时间片,每个任务每次只能运行一个“时间片”的时间长度就得退出给别 的任务运行,这样可以确保任何一个任务都不会霸占整个系统不放。这个心跳, 可以通过定时器来周期性触发,而这个定时器就是systick。很明显,这个“心跳” 是不允许任何人来随意地访问和修改的。只要不把它在SysTick控制及状态寄 存器中的使能位清除,就永不停息。它有四个寄存器:STK_CSR,0xE000E010-控制寄存器 STK_LOAD,0xE000E014-重载寄存器 STK_VAL,0xE000E018-当前值寄存器 STK_CALRB,0xE000E01C-校准值寄存器 1、STK_CSR控制寄存器:寄存器内有4个位t具有意义 第0位:ENABLE,Systick使能位(0:关闭Systick功能;1:开启Systick 功能) 第1位:TICKINT,Systick中断使能位(0:关闭Systick中断;1:开启 Systick中断) 第2位:CLKSOURCE,Systick时钟源选择(0:使用HCLK/8作为Systick 时钟;1:使用HCLK作为Systick时钟) 第3位:COUNTFLAG,Systick计数比较标志,如果在上次读取本寄存器后, SysTick已经数到了0,则该位为1。如果读取该位,该位将自动清零。 2、STK_LOAD重载寄存器: Systick是一个递减的定时器,当定时器递减至0时,重载寄存器中的值就 会被重装载,继续开始递减。STK_LOAD重载寄存器是个24位的寄存器最大 计数0xFFFFFF。 3、STK_VAL当前值寄存器: 也是个24位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同 时还会清除在SysTick控制及状态寄存器中的COUNTFLAG标志。 4、STK_CALRB校准值寄存器: 位31NOREF:1=没有外部参考时钟(STCLK不可用)0=外部参考时钟可用 位30SKEW:1=校准值不是准确的1ms0=校准值是准确的1ms 位23:0:Calibrationvalue IndicatesthecalibrationvaluewhentheSysTickcounterrunsonHCLK max/8asexternalclock.Thevalueisproductdependent,pleasereferto theProductReferenceManual,SysTickCalibrationValuesection.When HCLKisprogrammedatthemaximumfrequency,theSysTickperiodis1ms. Ifcalibrationinformationisnotknown,calculatethecalibrationvalue requiredfromthefrequencyoftheprocessorclockorexternalclock.2.systick编程 现在我们想通过Systick定时器做一个精确的延迟函数,比如让LED精确延 迟1秒钟闪亮一次。 思路:利用systick定时器为递减计数器,设定初值并使能它后,它会每个 系统时钟周期计数器减1,计数到0时,SysTick计数器自动重装初值并继续 计数,同时触发中断。 那么每次计数器减到0,时间经过了:系统时钟周期*计数器初值。我们 使用72M作为系统时钟,那么每次计数器减1所用的时间是1/72M,计数器 的初值如果是72000,那么每次计数器减到0,时间经过(1/72M)*72000= 0.001,即1ms。 Q:什么是SYSTick定时器? SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中 自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清 除,就永不停息。 Q:为什么要设置SysTick定时器? (1)产生操作系统的时钟节拍 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。 在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作 为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让 用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。 (2)便于不同处理器之间程序移植。 CortexM3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带 有这个定时器,软件在不同CM3器件间的移植工作得以化简。该定时器的时钟 源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟(C M3处理器上的STCLK信号)。 不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率 可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。Sys Tick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有 它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多 了,因为在所有CM3产品间对其处理都是相同的。 (3)作为一个闹铃测量时间。 SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹 铃,用于测量时间等。要注意的是,当处理器在调试期间被喊停(halt)时,则 SysTick定时器亦将暂停运作。 Q:Systick如何运行? 首先设置计数器时钟源,CTRL-CLKSOURCE(控制寄存器)。设置重载值(R ELOAD寄存器),清空计数寄存器VAL(就是下图的CURRENT)。置CTRL -ENABLE位开始计时。 如果是中断则允许Systick中断,在中断例程中处理。如采用查询模式则不断读 取控制寄存器的COUNTFLAG标志位,判断是否计时至零。或者采取下列一种 方法 当SysTick定时器从1计到0时,它将把COUNTFLAG位置位;而下述方法 可以清零之: 1.读取SysTick控制及状态寄存器(STCSR) 2.往SysTick当前值寄存器(STCVR)中写任何数据 只有当VAL值为0时,计数器自动重载RELOAD。Q:如何使用SysTicks作为系统时钟? SysTick的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要 这种“滴答”来推动任务和时间的管理。如欲使能SysTick异常,则把STCSR.TI CKINT置位。另外,如果向量表被重定位到SRAM中,还需要为SysTick异 常建立向量,提供其服务例程的入口地址.STM32的时钟树 对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。 众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固定的,用户无法将此时钟速率更改,除非更换晶振。 而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。标号 图1标号释义1 内部低速振荡器(LSI,40Khz)2 外部低速振荡器(LSE,32.768Khz)3 外部高速振荡器(HSE,3-25MHz)4 内部高速振荡器(HIS,8MHz)5 PLL输入选择位6 RTC时钟选择位7 PLL1分频数寄存器8 PLL1倍频寄存器9 系统时钟选择位10 USB分频寄存器11 AHB分频寄存器12 APB1分频寄存器13 AHB总线14 APB1外设总线15 APB2分频寄存器16 APB2外设总线17 ADC预分频寄存器18 ADC外设19 PLL2分频数寄存器20 PLL2倍频寄存器21 PLL时钟源选择寄存器22 独立看门狗设备23 RTC设备图1 STM32的时钟树 在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:35721891113对此条时钟路径做如下解析:对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);对于7,设置外部晶振的分频数(假设1分频);对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);对于8,设置PLL倍频数(假设9倍频);对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);对于11,设置AHB总线分频数(假设1分频);对于13,时钟到达AHB总线;在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:3572189111516对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);对于7,设置外部晶振的分频数(假设1分频);对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);对于8,设置PLL倍频数(假设9倍频);对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);对于11,设置AHB总线分频数(假设1分频);对于15,设置APB2总线分频数(假设1分频);对于16,时钟到达APB2总线;现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):1) 由3所知晶振输入为8MHz,由521知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。2) 由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。3) 时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。4) 时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。5) 时钟到达APB2总线外设。因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:void RCC_Configuration(void)ErrorStatus HSEStartUpStatus; (1)RCC_DeInit(); (2)RCC_HSEConfig(RCC_HSE_ON); (3)HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4)if(HSEStartUpStatus = SUCCESS) (5) RCC_HCLKConfig(RCC_SYSCLK_Div1); (6) RCC_PCLK2Config(RCC_HCLK_Div1); (7) RCC_PCLK1Config(RCC_HCLK_Div2); (8) FLASH_SetLatency(FLASH_Latency_2); (9) FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11)RCC_PLLCmd(ENABLE); (12)while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)=RESET); (13)RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)while(RCC_GetSYSCLKSource()!=0x08); (15)以上是ST官方所提供的STM32时钟树配置函数,读者首先要知道3点1、ST所提供的库函数在函数和变量命名上有非常良好的规范性和易读性(虽然有点冗长),即便没有注释,也可从函数名和变量名来大致判断该函数或变量所包含的意义。2、其次,读者应从上图区分出各个总线和对应的时钟:其中PLLCLK表示PLL锁相环的输出时钟,SYSCLK表示系统时钟,HCLK表示AHB总线的时钟,PCLK1表示APB1总线的时钟,PCLK2则表示APB2总线的时钟。3、9、10两句代码的作用是设置STM32内部FLASH的等待周期。做如下解释:STM32的内部用户FLASH用以存储代码指令供CPU存取以执行,STM32的CPU的最大速率已知为72MHz,但FLASH无法达到这么高的速度,因此要在CPU存取FLASH的过程中插入所谓的“等待周期”,显然CPU速度越快,所要插入的等待周期个数越多,原则是1)当CPU速率为0 24MHz时,不需要插入等待周期,即等到周期个数为0;2)当CPU速率为24 48MHz时,插入1个等待周期;3)当CPU速率为48MHz 72MHz时,插入2个等待周期;有以上三点准备之后,开始解析这段程序:(1)定义一个ErrorStatus类型的变量HSEStartUpStatus;(2)将时钟树复位至默认设置;(3)开启HSE晶振;(4)等待HSE晶振起振稳定,并将起振结果保存至HSEStartUpStatus变量中;(5)判断HSE晶振是否起振成功(假设成功了,进入if内部);(6)设置HCLK时钟为SYSCLK的1分频;(7)设置PLCK2时钟为SYSCLK的1分频;(8)设置PLCK1时钟为SYSCLK的2分频;(11)选择PLL输入源为HSE时钟经过1分频,并进行9倍频;(12)使能PLL输出;(13)等待PLL输出稳定;(14)选择系统时钟源为PLL输出;(15)等待系统时钟稳定;上述代码中对时钟树的配置顺序为(对应图中标号):311141572189通过对比发现,程序中对时钟树的配置顺序并不是依次从图中由左到右、由上到下配置的,这是为什么呢?事实上这个问题相信大部分读者都可以自己解释:电子设计世界的思维和操作方式,其顺序往往和日常生活是不一样的,比如人们经常先给电视机连接电源,再打开电视机开关;先把大水管的总闸打开,再打开小水龙头;总而言之是一种由“主”到“次”的顺序。转移到最常见的51单片机的开发平台,开发人员往往先把定时器的分频数,重载值等参数配置好,最后才启动定时器计数;先把各个外设的中断打开,最后再打开总中断;这和人们的生活习惯其实恰好相反,是一种先“次”后“主”的顺序。至此,理解STM32的时钟树就是轻而易举的事情了。STM32的IO口可以由软件配置成8种模式:1、输入浮空2、输入上拉3、输入下拉4、模拟输入5、开漏输出6、推挽输出7、推挽式复用功能8、开漏复用功能每个IO口可以自由编程,单IO口寄存器必须要按32位字被访问。STM32的很多IO口都是5V兼容的,这些IO口在与5V电平的外设连接的时候很有优势,具体哪些IO口是5V兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level标FT的就是5V电平兼容的)。STM32的每个IO端口都有7个寄存器来控制。他们分别是:配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的所存寄存器LCKR;这里我们仅介绍常用 的几个寄存器,我们常用的IO端口寄存器只有4个:CRL、CRH、IDR、ODR。CRL和CRH控制着每个IO口的模式及输出速率。STM32的IO口位配置表如表3.1.1.1所示: 表3.1.1.1 STM32的IO口位配置表STM32输出模式配置如表3.1.1.2所示: 表3.1.1.2 STM32输出模式配置表接下来我们看看端口低配置寄存器CRL的描述,如下图所示: 图3.1.1.1 端口低配置寄存器CRL各位描述该寄存器的复位值为0X4444 4444,从上图可以看到,复位值其实就是配置端口为浮空输入模式。从上图还可以得出:STM32的CRL控制着每个IO端口(AG)的低8位的模式。每个IO端口的位占用CRL的4个位,高两位为CNF,低两位为MODE。这里我们可以记住几个常用的配置,比如0X4表示模拟输入模式(ADC用)、0X3表示推挽输出模式(做输出口用,50M速率)、0X8表示上/下拉输入模式(做输入口用)、0XB表示复用输出(使用IO口的第二功能,50M速率)。CRH的作用和CRL完全一样,只是CRL控制的是低8位输出口,而CRH控制的是高8位输出口。这里我们对CRH就不做详细介绍了。给个实例,比如我们要设置PORTC的11位为上拉输入,12位为推挽输出。代码如下:GPIOC-CRH&=0XFFF00FFF;/清掉这2个位原来的设置,同时也不影响其他位的设置GPIOC-CRH|=0X00038000; /PC11输入,PC12输出GPIOC-ODR=111;/PC11上拉通过这3句话的配置,我们就设置了PC11为上拉输入,PC12为推挽输出。IDR是一个端口输入数据寄存器,只用了低16位。该寄存器为只读寄存器,并且只能以16位的形式读出。该寄存器各位的描述如下图所示: 图3.1.1.2 端口输入数据寄存器IDR各位描述要想知道某个IO口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起来是比较简单的。ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器虽然为可读写,但是从该寄存器读出来的数据都是0。只有写是有效的。其作用就是控制端口的输出。该寄存器的各位描述如下图所示: 图3.1.1.3 端口输出数据寄存器ODR各位描述#include #include led.h/Mini STM32开发板/LED驱动代码 /正点原子ALIENTEK/2010/5/27/ V1.0/初始化PA8和PD2为输出口.并使能这两个口的时钟 /LED IO初始化void LED_Init(void)RCC-APB2ENR|=1APB2ENR|=1CRH&=0XFFFFFFF0; GPIOA-CRH|=0X00000003;/PA8 推挽输出 GPIOA-ODR|=1CRL&=0XFFFFF0FF;GPIOD-CRL|=0X00000300;/PD.2推挽输出GPIOD-ODR|=12; /PD.2输出高 该代码里面就包含了一个函数void LED_Init(void),该函数的功能就是用来实现配置PA8和PD2为推挽输出。在配置STM32外设的时候,任何时候都要先使能该外设的时钟!APB2ENR是APB2总线上的外设时钟使能寄存器,其各位的描述如下: 图3.1.3.2 寄存器APB2ENR各位描述我们要使能的PORTA和PORTD的时钟使能位,分别在bit2和bit5,只要将这两位置1就可以使能PORTA和PORTD的时钟了。该寄存器还包括了很多其他外设的时钟使能。大家在以后会慢慢使用到的。关于这个寄存器的详细说明在STM32参考手册的第61页。在设置完时钟之后就是配置完时钟之后,LED_Init配置了PA8和PD2的模式为推挽输出,并且默认输出1。这样就完成了对这两个IO口的初始化。保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。在led.h中输入如下代码:#ifndef _LED_H#define _LED_H #include sys.h/Mini STM32开发板/LED驱动代码 /正点原子ALIENTEK/2010/5/27/LED端口定义#define LED0 PAout(8)/ PA8#define LED1 PDout(2)/ PD2void LED_Init(void);/初始化 #endif这段代码里面最关键就是2个宏定义:#define LED0 PAout(8)/ PA8#define LED1 PDout(2)/ PD2这里使用的是位带操作来实现操作某个IO口的1个位的,关于位带操作前面已经有介绍,这里不再多说。需要说明的是,这里可以使用另外一种操作方式实现。如下:#defineLED0 (18) /led0 PA8#defineLED1 (1ODR=(GPIOA-ODR&LED0)|(x ? LED0:0)#define LED1_SET(x) GPIOD-ODR=(GPIOD-ODR&LED1)|(x ? LED1:0)后者通过LED0_SET(0)和LED0_SET(1)来控制PA8的输出0和1。而前者的类似操作为:LED0=0和LED0=1。显然前者简单很多,从而可以看出位带操作带来的好处。以后像这样的IO口操作,我们都使用位带操作来实现,而不使用第二种方法。STM32的串口是相当丰富的,功能也很强劲。最多可提供5路串口(MiniSTM32使用的是STM32F103RBT6,具有3个串口),有分数波特率发生器、支持单线光通信和半双工单线通讯、支持LIN、智能卡协议和IrDASIR ENDEC规范(仅串口3支持)、具有DMA等。 串口最基本的设置,就是波特率的设置。STM32的串口使用起来还是蛮简单的,只要你开启了串口时钟,并设置相应IO口的模式,然后配置一下波特率,数据位长度,奇偶校验位等信息,就可以使用了。下面,我们就简单介绍下这几个与串口基本配置直接相关的寄存器。1,串口时钟使能。串口作为STM32的一个外设,其时钟由外设时钟使能寄存器控制,这里我们使用的串口1是在APB2ENR寄存器的第14位。APB2ENR寄存器在之前已经介绍过了,这里不再介绍。只是说明一点,就是除了串口1的时钟使能在APB2ENR寄存器,其他串口的时钟使能位都在APB1ENR。2,串口复位。当外设出现异常的时候可以通过复位寄存器里面的对应位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。串口1的复位是通过配置APB2RSTR寄存器的第14位来实现的。APB2RSTR寄存器的各位描述如下: 图3.3.1.1寄存器APB2RSTR各位描述从上图可知串口1的复位设置位在APB2RSTR的第14位。通过向该位写1复位串口1,写0结束复位。其他串口的复位位在APB1RSTR里面。3,串口波特率设置。每个串口都有一个自己独立的波特率寄存器USART_BRR,通过设置该寄存器达到配置不同波特率的目的。该寄存器的各位描述如下: 图3.3.1.2寄存器USART_BRR各位描述前面提到STM32的分数波特率概念,其实就是在这个寄存器里面体现的。最低4位用来存放小数部分DIV_Fraction,15:4这12位用来存放整数部分DIV_Mantissa。高16位未使用。这里波特率的计算通过如下公式计算:这里的fpclkx(x=1、2)是给外设的时钟(PCLK1用于串口2、3、4、5,PCLK2用于串口1),USARTDIV是一个无符号的定点数,它的值可以有串口的BRR寄存器值得到。而我们更关心的是如何从USARTDIV的值得到USART_BRR的值,因为一般我们知道的是波特率,和PCLKx的时钟,要求的就是USART_BRR的值。下面我们来介绍如何通过USARTDIV得到串口USART_BRR寄存器的值,假设我们的串口1要设置为9600的波特率,而PCLK2的时钟为72M。这样,我们根据上面的公式有:USARTDIV=72000000/9600*16=468.75那么得到:DIV_Fraction=16*0.75=12=0X0C; DIV_Mantissa= 468=0X1D4;这样,我们就得到了USART1-BRR的值为0X1D4C。只要设置串口1的BRR寄存器值为0X1D4C就可以得到9600的波特率。4,串口控制。STM32的每个串口都有3个控制寄存器USART_CR13,串口的很多配置都是通过这3个寄存器来设置的。这里我们只要用到USART_CR1就可以实现我们的功能了。BIT 13: 串口功能;BIT 12: MODE,字长。0:1个开始位,8个数据位,1位停止位(默认);1:1个开始位,9位数据位,1位停止位(默认);*注意:停止位的长度可在USART_CR2寄存器中设置。BIT 11: WAKE 唤醒功能BIT 10: 校检使能位,当激活奇偶校验功能时,置位该位将自动往要传输数据的高位字节处插入就校验位。BIT 09: Parity Selection,0:偶校验;1:奇校验。BIT 08: PE Interrupt EnableBIT 07: 发送缓冲区空中断使能位BIT 06: 发送完成中断使能位BIT 05: 接收缓冲区非空中断使能位BIT 04: Idle Interrupt EnableBIT 03: Transfer EnableBIT 02: Receive EnableBIT 01: Receiver WakeupBIT 00: Send Break5,数据发送与接收。STM32的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到收据的时候,也是存在该寄存器内。该寄存器的各位描述如下: 图3.3.1.3寄存器USART_DR各位描述可以看出,虽然是一个32位寄存器,但是只用了低9位(DR8:0),其他都是保留。DR8:0为串口数据,包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的并行接口。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。当使能校验位(USART_CR1种PCE位被置位)进行发送时,写到MSB的值(根据数据的长度不同,MSB是第7位或者第8位)会被后来的校验位该取代。当使能校验位进行接收时,读到的MSB位是接收到的校验位。6,串口状态。串口的状态可以通过状态寄存器USART_SR读取。USART_SR的各位描述如下: 图3.3.1.4寄存器USART_SR各位描述 这里我们关注一下两个位,第5、6位RXNE和TC。RXNE(读数据寄存器非空),当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取USART_DR,通过读USART_DR可以将该位清零,也可以向该位写0,直接清除。TC(发送完成),当该位被职位的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:1)读USART_SR,写USART_DR。2)直接向该位写0。/初始化IO 串口1/pclk2 CLK2时钟频率(Mhz)/bound:波特率void uart_init(u32 pclk2,u32bound)floattemp;u16mantissa;u16fraction;temp=(float)(pclk2*1000000)/(bound*16);/得到USARTDIVmantissa=temp;/得到整数部分fraction=(temp-mantissa)*16;/得到小数部分mantissaAPB2ENR|=1APB2ENR|=1CRH=0X444444B4;/IO状态设置RCC-APB2RSTR|=1APB2RSTR&=(1BRR=mantissa; / 波特率设置USART1-CR1|=0X200C;/1位停止,无校验位.#ifdef EN_USART1_RX/如果使能了接收/使能接收中断USART1-CR1|=1CR1|=1BRR的内容。然后开始初始化串口引脚,接着把USART1复位,然之后设置波特率和奇偶校验等。这里需要注意一点,因为我们使用到了串口的中断接收,必须在usart.h里面定义 EN_USART1_RX 。该函数才会配置中断使能,以及开启串口1的NVIC中断。这里我们把串口1中断放在组2,优先级设置为组2里面的最低。再介绍一下串口1的中断服务函数USART1_IRQHandler,该函数的名字不能自己定义了,MDK已经给每个中断都分配了一个固定的函数名,我们直接用就可以了。具体这些函数的名字是什么,我们可以在MDK提供的例子里面,找到stm32f10x_it.c,该文件里面包含了STM32所有的中断服务函数。USART1_IRQHandler的代码如下:void USART1_IRQHandler(void)u8res;if(USART1-SR&(1DR;if(USART_RX_STA&0x80)=0)/接收未完成if(USART_RX_STA&0x40)/接收到了0x0dif(res!=0x0a)USART_RX_STA=0;/接收错误,重新开始elseUSART_RX_STA|=0x80;/接收完成了 else /还没收到0X0Dif(res=0x0d)USART_RX_STA|=0x40;elseUSART_RX_BUFUSART_RX_STA&0X3F=res;USART_RX_STA+;if(USART_RX_STA63)USART_RX_STA=0;/接收数据错误,重新开始接收该函数的重点就是判断接收是否完成,通过检测是否收到0X0D、0X0A的连续2个字节(回车键)来检测是否结束。当检测到这个结束序列之后,就会置位USART_RX_STA的最高为来标记已经收到了一次数据。之后等待外部函数清空该位之后才开始第二次接收。所接收的数据全部存放在USART_RX_BUF里面,一次接收数据不能超过64个字节,否则被丢弃。介绍完了这两个函数,我们回到test.c,在test.c里面编写如下代码:#include #include sys.h#include usart.h#include delay.h#include led.h #include key.h/Mini STM32开发板范例代码3/串口实验/正点原子ALIENTEK/2010.5.28int main(void)u8t;u8len;u16times=0;Stm32_Clock_Init(9);/系统时钟设置delay_init(72);/延时初始化 uart_init(72,9600);/串口初始化为9600LED_Init();/初始化与LED连接的硬件接口while(1)if(USART_RX_STA&0x80)len=USART_RX_STA&0x3f;/得到此次接收到的数据长度printf(n您发送的消息为:n);for(t=0;tDR=USART_RX_BUFt;while(USART1-SR&0X40)=0);/等待发送结束printf(nn);/插入换行USART_RX_STA=0;elsetimes+;if(times%5000=0)printf(nMiniSTM32开发板串口实验n);printf(正点原子ALIENTEKnnn);if(times%200=0)printf(请输入数据,以回车键结束n);if(times%30=0)LED0=!LED0;/闪烁LED,提示系统正在运行.delay_ms(10);这段代码比较简单,重点看下以下两句:USART1-DR=USART_RX_BUFt;while(USART1-SR&0X40)=0);/等待发送结束第一句,其实就是发送一个字节到串口,通过直接操作寄存器来实现的。第二句呢,就是我们在写了一个字节在USART1-DR之后,要检测这个数据是否已经被发送完成了,通过检测USART1-SR的第6位,是否为1来决定是否可以开始第二个字节的发送。寄存器:1.串口1的时钟使能在APB2ENR寄存器第14位(其他串口的时钟使能位都在APB1ENR)。2.串口1的复位是通过配置APB2RSTR寄存器的第14位(其他串口的复位位在APB1RSTR里面)3.波特率寄存器USART_BRR(低16位有效)4.控制寄存器USART_CR13:低14位有效;位13(UE)是串口使能位,位3(TE)是发送使能位,位2(RE)是接受使能位5.数据寄存器USART_DR(低9位有效,兼具读写功能)6.状态寄存器USART_SR:5位RXNE(读数据寄存器非空),6位TC(发送完成)(转帖)STM32的每个IO口都可以作为中断输入,这点很好用。要把IO口作为外部中断输入,有以下几个步骤:1)初始化IO口为输入。这一步设置你要作为外部中断输入的IO口的状态,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算使用了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。2)开启IO口复用时钟,设置IO口与中断线的映射关系。STM32的IO口与中断线的对应关系需要配置外部中断配置寄存器EXTICR,这样我们要先开启复用时钟,然后配置IO口与中断线的对应关系。才能把外部中断与中断线连接起来。3)开启与该IO口相对的线上中断/事件,设置触发条件。这一步,我们要配置中断产生的条件,S

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论