【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图)

cathy的头像

STM32的GPIO介绍

STM32引脚说明

GPIO是通用输入/输出端口的简称,是STM32可控制的引脚。GPIO的引脚与外部硬件设备连接,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。

STM32F103ZET6芯片为144脚芯片,包括7个通用目的的输入/输出口(GPIO)组,分别为GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF、GPIOG,同时每组GPIO口组有16个GPIO口。通常简略称为PAx、PBx、PCx、PDx、PEx、PFx、PGx,其中x为0-15。

STM32的大部分引脚除了当GPIO使用之外,还可以复用位外设功能引脚(比如串口),这部分在【STM32】STM32端口复用和重映射(AFIO辅助功能时钟) 中有详细的介绍。

GPIO基本结构

每个GPIO内部都有这样的一个电路结构,这个结构在本文下面会具体介绍。

这边的电路图稍微提一下:

(1)测量脉冲信号频率fo,频率范围为10Hz~2MHz,测量误差的绝对值不大于0.1%。

(2)测量脉冲信号占空比D,测量范围为10%~90%,测量误差的绝对值不大于2%。

使用官方STM32F429 Discovery开发板,主频180MHz,定时器频率90MHz。

思路一、外部中断

这种方法是很容易想到的,而且对几乎所有MCU都适用(连51都可以)。方法也很简单,声明一个计数变量TIM_cnt,每次一个上升沿/下降沿就进入一次中断,对TIM_cnt++,然后定时统计即可。如果需要占空比,那么就另外用一个定时器统计上升沿、下降沿之间的时间即可。

缺陷显而易见,当频率提高,将会频繁进入中断,占用大量时间。而当频率超过100kHz时,中断程序时间甚至将超过脉冲周期,产生巨大误差。同时更重要的是,想要测量的占空比由于受到中断程序影响,误差将越来越大。

笔者当时第一时间就把这个方案PASS了,没有相关代码(这个代码也很简单)。不过,该方法在频率较低(10kHz以下)时,可以拿来测量频率。在频率更低的情况下,可以拿来测占空比。

思路二、PWM输入模式

翻遍ST的参考手册,在定时器当中有这样一种模式:

“”

简而言之,理论上,通过这种模式,可以用硬件直接测量出频率和占空比。当时我们发现这一模式时欢欣鼓舞,以为可以一步解决这一问题。

但是,经过测量之后发现这种方法测试数据不稳定也不精确,数据不停跳动,且和实际值相差很大。ST的这些功能经常有这种问题,比如定时器的编码器模式,在0点处频繁正负跳变时有可能会卡死。这些方法虽然省事,稳定性却不是很好。

经过线性补偿可以一定程度上减少误差(参数在不同情况下不同):

freq=Frequency×2.2118-47.05

这种方法无法实现要求。所以在这里笔者并不推荐这种方法。

思路三、输入捕获

一般来说,对STM32有一定了解的人在测量频率的问题上往往都会想到利用输入捕获。

首先设定为上升沿触发,当进入中断之后(rising)记录与上次中断(rising_last)之间的间隔——周期,其倒数就是频率。

再设定为下降沿,进入中断之后与上升沿时刻之差即为高电平时间(falling-rising_last),高电平时间除周期即为占空比。

“”

该方法尤其是在中低频(<100kHz)之下精度不错。

缺点是该方法仍然会带来极高的中断频率。在高频之下,首先是CPU时间被完全占用,此外,更重要的是,中断程序时间过长往往导致会错过一次或多次中断信号,表现就是测量值在实际值、实际值×2、实际值×3等之间跳动。实测中,最高频率可以测到约400kHz。

该方法在低频率(<100kHz)下有着很好的精度,在考虑到其它程序的情况下,建议在10kHz之下使用该方法。同时,可以参考以下的改进程序减少CPU负载。

改进方案

前述问题,限制频率提高的主要因素是过长的中断时间,一般应用情景之下,还有其它程序部分的限制。所以需要进行改进。

方案一

1.使用2个通道,一个只测量上升沿,另一个只测量下降沿。这样可以减少切换触发边沿的延迟,缺点是多用了一个IO口。

2.使用寄存器,简化程序

之所以改用TIM2是因为TIM5的CH1(PA0)还是按键输入引脚。本来想来这应当也没什么,按键不按下不就是开路嘛。所以,当使用别人的程序之前,请一定仔细查看电路图。

“”

这样,最高频率能够达到约1.1MHz,是一个不小的进步。但是,其根本问题,中断太频繁,仍然存在。

解决思路也是存在的。本质上,实际只需要读取CCR1和CCR2寄存器。而在内存复制过程中,面对大数据量的转移时,会想到什么?

显然,很容易想到——利用DMA。所以,笔者使用输入捕获事件触发DMA来搬运寄存器而非触发中断即可,然后将这些数据存放在一个数组当中并循环刷新。这样,可以随时来查看数据并计算出频率。

方案二

1.可以设定仅有通道2进行下降沿捕获并触发中断,而通道1捕获上升沿不触发中断。在中断函数当中,一次读取CCR1和CCR2。这样可以节省大量时间。

2.可以先进行一次测量,根据测量值改变预分频值PSC,从而提高精度

3.间隔采样。例如每100ms采样10ms.

这样的改进应当能够将最高采样频率增加到2M.但是频率的进一步提高仍然不可能。

因为这时的主要矛盾是中断函数时间过长,导致CPU还在处理中断的时候这一次周期就结束了,使得最终测量到的频率为真实频率的整数倍左右。示意图如下:

“”

结 语

外部中断:编写容易,通用性强。缺点是中断进入频繁,误差大。

PWM输入:全硬件完成,CPU负载小,编写容易。缺点是不稳定,误差大。

输入捕获:可达到约400kHz。低频精度高,10kHz可达到0.01%以下,400kHz也有3%。缺点是中断频繁,无法测量高频,幅值必须在3.3~5V之间。

本文转载自:网络
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

点击这里,获取更多关于应用和技术的有关信息
点击这里,获取更多工程师博客的有关信息

围观 34

1、AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2>1,意思是APB2接高速设备

2、Stm32f10x.h相当于reg52.h(里面有基本的位操作定义),另一个为stm32f10x_conf.h专门控制外围器件的配置,也就是开关头文件的作用

3、HSE Osc(High Speed External Oscillator)高速外部晶振,一般为8MHz,HSI RC(High Speed InternalRC)高速内部RC,8MHz

4、LSE Osc(Low Speed External Oscillator)低速外部晶振,一般为32.768KHz,LSI RC(Low Speed InternalRC)低速内部晶振,大概为40KHz左右,提供看门狗时钟和自动唤醒单元时钟源

5、SYSCLK时钟源有三个来源:HSI RC、HSE OSC、PLL

“”

6、MCO[2:0]可以提供4源不同的时钟同步信号,PA8

7、GPIO口貌似有两个反向串联的二极管用作钳位二极管。

“”

8、总线矩阵采用轮换算法对系统总线和DMA进行仲裁

9、ICode总线,DCode总线、系统总线、DMA总线、总线矩阵、AHB/APB桥

10、在使用一个外设之前,必须设置寄存器RCC_AHBENR来打开该外设的时钟

11、数据字节以小端存储形式保存在存储器中

12、内存映射区分为8个大块,每个块为512MB

13、FLASH的一页为1K(小容量和中容量),大容量是2K。

14、系统存储区(SystemMemory)为ST公司出厂配置锁死,用户无法编辑,用于对FLASH区域进行重新编程。所以我们烧写程序务必选择BOOT1 = 0,这样通过内嵌的自举程序对FLASH进行烧写,比如中断向量表和代码

15、STM32核心电压为1.8V

16、STM32复位有三种:系统复位、上电复位、备份区域复位。其中系统复位除了RCC_CSR中的复位标志和BKP中的数值不复位之外,其他的所有寄存器全部复位。触发方式例如外部复位、看门狗复位、软件复位等;电源复位由于外部电源的上电/掉电复位或者待机模式返回。复位除了BKP中的寄存器值不动,其他全部复位;备份区域复位的触发源为软件复位或者VDD和VBAT全部掉电时。

17、单片机复位后所有I/O口均为浮空输入状态

18、68个可屏蔽中断通道,16个可编程优先级,16个内核中断,一共68+16=84个中断。103系列只有60个中断,107系列才有68个中断

19、系统启动从0x00000004开始,0x000 0000保留

20、(NestedVectored Interrupt Controller)NVIC嵌套向量中断控制器,分为两种:抢先式优先级(可嵌套)和中断优先级(副优先级,不能嵌套)。两种优先级由4位二进制位决定。分配下来有十六种情况:

“”

21、0号抢先优先级的中断,可以打断任何中断抢先优先级为非0号的中断;1号抢先优先级的中断,可以打断任何中断抢先优先级为2、3、4号的中断;……;构成中断嵌套。如果两个中断的抢先优先级相同,谁先出现,就先响应谁,不构成嵌套。如果一起出现(或挂在那里等待),就看它们2个谁的子优先级高了,如果子优先级也相同,就看它们的中断向量位置了。原来中断向量的位置是最后的决定因素!!!!

22、上电初始化后AIRC初始化为0,为16个抢先式优先级,但是由于所有的外部通道中断优先级控制字PRI_n为0,所以抢先式优先级相同,此时就不能嵌套了

23、NVI中有ISER[2](Interrupt Set-Enable Registers),ICER[2](Interrupt Clear-Enable Registers),ISPR[2](Interrupt Set-Pending Registers),ICPR[2](Interrupt Clear-Pending Registers),IABR[2](Active Bit Registers),IPR[15](InterruptPriority Registers)定义。其中ISER和ICER分别为中断使能和中断失能寄存器,都是写1来使能/失能中断的。为什么写1?为什么不采用一个寄存器而用两个寄存器来表示中断使能/失能状态?由于硬件,写0比较复杂,并且可能造成其他位的状态改变,所以用1来表示打开或者关断是比较合理的

24、中断标志位需要手动清除

25、配置外围器件的一般步骤:1、打开端口时钟。2、定义初始化结构体并初始化。3、调用

26、串口的奇偶校验:如果是奇偶校验,那么USART_InitStructure.USART_WordLength= USART_WordLength_9b;这个数据的长度必须设定为9位!

27、ADC的规则组可以自定义转换通道顺序和转换的通道个数。在实际应用中,有时候希望有一些特别的通道具有很高的优先权,需要在规则组进行转换的时候强制打断,进行另一个通道的转换,这样一组通道,叫做注入组。

28、定时器的输出比较模式:Timing(冻结,什么都不做,普通定时),Active(OCxREF输出高电平有效),Inactive(OCxREF输出低电平),Toggle(比较成功后翻转电平)。

29、STM32的定时器从0开始计数,满足一些条件,给出标志位(比如匹配成功、时间更新、溢出等)然后从0开始计数。这一点和51不同。

30、OCx=OCxREF+极性

31、自动装载寄存器和影子寄存器:前者相当于51当中的溢出设定数值。而影子寄存器顾名思义是影子,就是寄存器的另一分copy。实际起作用的是影子寄存器,而程序员操纵的则是自动装载寄存器。如果APPE位使能,表明自动装载寄存器的值在下一次更新事件发生后才写入新值。否则,写入自动装载寄存器的值会被立即更新到影子寄存器。

“”

32、RCC_PCLK1Config(RCC_HCLK_Div4)CLK1的4分频给定时器基准时钟

33、定时器配置:RCC、NVIC、GPIO(OC输出或者PWM)、TIMx

34、通用定时器可以输出4路不同的PWM,高级定时器可以输出4路不同的PWM外,还可以输出3路互补的PWM信号(驱动三相电机),一共有7路。这样算出来STM32可以产生30路PWM=7*2+4*4

35、

“”

36、高级定时器时钟源挂在了APB2上,而通用定时器挂在APB1上。AHB(72mhz)→APB1分频器(默认2)→APB1时钟信号(36mhz)→倍频器(*2倍)→通用定时器时钟信号(72mhz)。如果APB1没有分频,那么通用定时器的时钟信号频率就直接等于APB1的时钟频率,没有上述的倍频器*2过程。TIM_SetAutoreload()用来改变PWM的频率,TIM_SetCompare1()用来改变占空比

37、有刷电机一般启动力矩大一些,无刷电机启动力矩小,运行起来力矩大。有刷电机采用电刷机械电流换向,而无刷电机则通过霍尔传感器测出转子的电流来判断电机的运动位置和方向,返回给控制回路。

38、死区是必须要有的,因为这涉及到电路的短路问题。晶闸管在换向的时候需要死区时间来彻底关断线路

39、刹车功能用来在控制回路出现问题时,硬件自动给予外部电机进行紧急刹车制动,反应在PWM上持续给出一个固定的占空比?(三相驱动也是?)

40、PWM输出最好采用PWM模式,其他的比较输出模式相位会慢慢改变,不精准

41、对FLASH的读写需要先解锁后加锁。FLASH写0容易,写1难。

42、下载程序有两种方式,一种为ICP(在线编程),适用于JTAG或SWD协议下的烧写程序。另一种成为IAP(在应用编程),适用于很多接口(USB,串口,CAN)下载到存储器中,IAP允许在程序运行时重新烧写FLASH

43、FLASH分为主存储器(这里放置用户的程序代码)和信息块(启动代码),除此之外,还有一部分叫做系统存储器,这一块用户不可操作,为ST公司出产后固化,为系统的上电自举程序

44、FLASH在写的时候,一定不能读,如果有读操作,那么将会锁住总线

45、对FLASH操作时,必须打开HIS

46、STM32有两种看门狗(IWDG独立看门狗《独立时钟》,WWDG窗口看门狗《由APB1分频而来》)

47、SPI的的最高频率为36MHz(fpclk/2)

48、 TIM1和TIM8高级定时器在输出PWM时,需要配置一下主输出功能(CtrlPWMOutputs)才能输出PWM。其他的通用定时器不需要这样配置。但是TIM6和TIM7没有PWM输出功能。

49、Code为程序代码部分
RO-data 表示程序定义的常量(如:const temp等);
RW-data 表示已初始化的全局变量
ZI-data 表示未初始化的全局变量,以及初始化为0的变量
Code, RO-data,RW-data..............flash
RW-data, ZIdata...................RAM
初始化时RW-data从flash拷贝到RAM

50、STM32F103ZET6有144个引脚(Z为144),其中,可用IO口为112个(7X16=112,ABCDEFG口)

本文转载自:电子发烧友论坛
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

点击这里,获取更多关于应用和技术的有关信息
点击这里,获取更多工程师博客的有关信息

围观 10

STM32家族中的所有芯片都内置了逐次逼近寄存器型ADC模块.内部大致框架如下:

“”

每次ADC转换先进行采样保持,然后分多步执行比较输出,步数等于ADC的位数,每个ADC时钟产生一个数据位。说到这里,用过STM32 ADC的人是不是想到了参考手册中关于12位ADC转换时间的公式:

“”

ST官方就如何保障或改善ADC精度写了一篇应用笔记AN2834。该应用笔记旨在帮助用户了解ADC误差的产生以及如何提高ADC的精度。主要介绍了与ADC设计的相关内容,比如外部硬件设计参数,不同类型的ADC误差来源分析等,并提出了一些如何减小误差的设计上建议。

这里我摘取部分内容,结合个人的理解加以整理与大家分享。更多细节可以去www.st.com搜索AN2834下载细看。

当我们在做STM32的ADC应用遇到转换结果不如意时,常有人提醒或建议你对采样时间或外部采样电路做调整。这里调整的最终目的就是让信号进入ADC模块的充电时间与内部采样时间匹配,保证采得的电压尽量真实,最终得到符合精度要求的转换结果。下面就聊聊相关话题。

一、模拟信号源阻抗的影响

“”

在做ADC操作时,在信号源与ADC引脚之间,或者说在串行电阻RAIN与ADC引脚AIN之间总有电流流过,自然会产生压降。内部采样电容CADC的充电由阻容网络中的开关和RADC控制。

显然,对CADC有效的充电由【RADC +RAIN】控制,充电时间常数是tc = (RADC + RAIN) ×CADC。不难理解如果采样时间小于CADC通过RADC +RAIN充电的时间,即ts < tc,则ADC转换得到的数值会小于实际数值。

可以看出,随着电阻(RADC+RAIN)的增加,对保持电容的充电时间也需要相应增加。对于STM32而言,RADC是内部的采样开关电阻,阻值相对固定,具体数值在芯片的数据手册里有给出。所以,这里真正可能变动的电阻就是信号源电阻RAIN了,它的变化影响充电常数,进而影响到芯片内部采样时间的选择。

注:tc是电容CADC充电完全的时间,此时Vc = VAIN(最大1/2LSB 误差)

Vc:采样电容CADC上的电压

tc = (RADC + RAIN) × CADC 【CADC的值也是相对固定的】

二、信号源的容抗与PCB分布电容的对ADC的影响

做ADC时,除了考虑信号源端的电阻外,还需要考虑信号源本身容抗和在模拟输入端的分布电容(参见下图)。信号源的电阻和电容构成一个阻容网络,如果外部的电容(CAIN +Cp)没能完全充电至输入信号电压,ADC转换的结果显然是不准确的。(CAIN + Cp)的值越大,信号源的频率也就越受限制。(信号源上的外部电容和分布电容分别用CAIN和Cp表示。)

“”

当外部电路的电容没有被模拟信号源完全充电的情况下,模拟输入信号电压与模拟输入脚的电压VAIN就不相同。如果模拟输入信号产生变化,它的变化频率(FAIN)的周期至少应该满足10RC原理,即10 × RAIN × (CAIN +Cp)。

TAIN = 模拟信号的周期 =1/FAIN

因为:TAIN ≥ 10 x RAIN x (CAIN + CP)

因此:FAIN ≤ 1 / [10 x RAIN x (CAIN+ CP)]

假如:RAIN = 25kΩ,CAIN = 7pF,CP = 3pF,则:

FAINmax = 1 / [10 x 25x103 x (7 +3)x10-12]

即信号源的最高频率FAIN(max)= 400kHz。

对于上述给出的信号源特性(容抗与阻抗),它的频率不能超过400kHz,否则ADC的转换结果将是不准确的。

三、模拟信号源的阻抗估算

上面聊了信号源阻抗和AD输入端的电容对ADC的影响,在这个基础上来看下最大允许信号源阻抗的估算。假定最大允许的误差是1/2 LSB。

参照上面的图9中的描述,假定此时输入端不存在输入信号充电不充分的情况。

“”

这样得到误差 = VAIN – Vc 【Vc为内部采样电容CADC上的电压】

“”

这里tS是采样时间。

tS = TS / fADC,其中TS是以ADC时钟周期为单位的采样时钟个数 。

对于给定的tS,对应VAIN = VREF+的误差要大于对应VAIN< VREF+时的误差,这是因为把CADC从0V充电至VAIN,VAIN = VREF+时需要比在VAIN < VREF+时需要更多的充电时间。因此VAIN = VREF+时是计算最大允许信号源阻抗时需要考虑的最坏情况,此时误差为1/2LSB.

“”

Rmax = (RAIN + RADC)max

这里N 是 ADC 分辨率 12

我们可以进一步得到:

“”

以STM32F103芯片为例,如果这里Ts=7.5, fADC=14MHz,Cadc=12pF,RadcMax=1K,在误差为1/2LSB时的最大允许信号源阻抗为:RAIN(max) = 6.4 k

通过上面的分析,我们可以得知选择较长的采样时间或较慢的ADC时钟,能保障更好的转换效果。或者通过降低ADC时钟频率、选择较长采样时间或选择较低的分辨率,可以容许更大的外部信号源阻抗。

注:如果使用跟随放大器可以减小信号源的阻抗效应,因为放大器的高输入阻抗和非常低的输出阻抗把RAIN与RADC隔离开来。当然,放大器自身引入的偏移误差也应加以注意和考虑。

上面提到的RadcMax、Cadc参数在芯片数据手册的ADC特性表格里,我把STM32F103的截图如下。好,先就聊到这里。整个完整应用笔记请前往官网搜索AN2834,其中有不少关于提高或改善ADC精度的建议,比如关于电源的处理等。

“”

本文转载自:STM32/8 单片机论坛
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

围观 29

什么是FPGA?

一、FPGA原理

FPGA中的基本逻辑单元是CLB模块,一个CLB模块一般包含若干个基本的查找表、寄存器和多路选择器资源,因此FPGA中的逻辑表达式基于LUT的。

FPGA内部的编程信息一般存储在SRAM单元中,因此通常的FPGA都是基于SRAM的,所以掉电后信息会丢失,下次上电需要先配置才能使用。

二、FPGA产品的速度等级

速度等级一般反映一款芯片的性能,速度等级越高,说明芯片内的逻辑延时和布线延时越小,设计的性能要求也越容易达到,随之付出的成本也越大。

对Xilinx FPGA,速度等级一般有“-1”、“-2”、“-3”等,数字越大,速度等级越高,芯片价钱也越贵。

对Intel FPGA,速度等级一般有“-6”、“-7”、“-8”,数字越小表示速度等级越高、价钱越贵。

三、FPGA内部资源

逻辑资源块是FPGA内部最重要的资源,Xilinx称其为CLB(configurable logic block);7系列中,FPGA内部三大主要资源:可编程逻辑单元、可编程I/O单元、布线资源。

“”

1、可配置逻辑单元(configurable logic block)

CLB在FPGA中最为丰富,由两个SLICE构成,SLICE分为SLICEL(L:Logic)和SLICEM(M:Memory),因此CLB可分为CLBLL和CLBLM两类;

“”

SLICEL和SLICEM内部都包含4个6输入查找表(LUT6)、3个数据选择器(MUX)、1个进位链(carry chain)和8个触发器(Flip-Flop);

“”

2、存储单元(Block RAM)

Block RAM可以被配置为同步、异步、单端口、双端口的RAM或FIFO,或者ROM;

3、运算单元(DSP48E1)

当FPGA需要复杂的运算时,会使用DSP48E1,例如乘法;

什么是STM32?

STM32系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex®-M0,M0+,M3, M4和M7内核。

它具有以下三个特点:

•新的基于ARM内核的32位MCU系列

–标准的ARM架构 –内核为ARM公司为要求高性能、低成本、低功耗的嵌入式应用专门设计的Cortex-M内核

•超前的体系结构

–高性能 –低电压 –低功耗 –创新的内核以及外设

•简单易用/自由/低风险

Cortex-M3系列属于ARMv7架构

ARMv7架构定义了三大分工明确的系列:

“A”系列:面向尖端的基于虚拟内存的操作系统和用户应用

“R”系列:针对实时系统;

“M”系列:对微控制器。

STM32F1属于Cortex-M系列中的Cortex-M3内核,采用ARMv7-M架构。STM32F4属于Cortex-M4系列采用ARMv7-ME架构。Cortex-A5/A8采用ARMv7-A架构。传统的ARM7系列采用的是ARMv4T架构。

“”

那么STM32与ARM有什么区别呢?

“”

STM32和ARM有什么区别

STM32的命名规则

“”

STM32的应用场景

“”

STM32的应用场景还有很多,这里只是简单地列出一些。

总的来说STM32具有如下优势:

1)极高的性能:主流的Cortex内核。

2)丰富合理的外设,合理的功耗,合理的价格。

3)强大的软件支持:丰富的软件包。

4)全面丰富的技术文档。

5)芯片型号种类多,覆盖面广。

6)强大的用户基础:最先成功试水CM3芯片的公司,积累了大批的用户群体,为其领先做铺垫。

本文转载自:电子发烧友网
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:
cathy@eetrend.com 进行处理。

围观 51

一、如何学习嵌入式系统- - 嵌入式系统的概念

着重理解“嵌入”的概念 ,主要从三个方面上来理解。

1、从硬件上,“嵌入”将基于CPU的处围器件,整合到CPU芯片内部,比如早期基于X86体系结构下的计算机,CPU只是有运算器和累加器的功能,一切芯片要造外部桥路来扩展实现,象串口之类的都是靠外部的16C550/2的串口控制器芯片实现,而目前的这种串口控制器芯片早已集成到CPU内部,还有PC机有显卡,而多数嵌入式处理器都带有LCD控制器,但其种意义上就相当于显卡。

比较高端的ARM类Intel Xscale架构下的IXP网络处理器CPU内部集成PCI控制器(可配成支持4个PCI从设备或配成自身为CPI从设备);还集成3个NPE网络处理器引擎,其中两个对应于两个MAC地址, 可用于网关交换用,而另外一个NPE网络处理器引擎支持DSL,只要外面再加个PHY芯片即可以实现DSL上网功能。

IXP系列最高主频可以达到 1.8G,支持2G内存,1G×10或10G×1的以太网口或Febre channel的光通道。IXP系列应该是目标基于ARM体系统结构下由 intel进行整合后成Xscale内核的最高的处理器了。

2、从软件上前,嵌入就是在定制操作系统内核里将应用一并选入,编译后将内核下载到ROM中。而在定制操作系统内核时所选择的应用程序组 件就是完成了软件的“嵌入”,比如WinCE在内核定制时,会有相应选择,其中就是wordpad,PDF,MediaPlay等等选择,如果我们选择 了,在CE启动后,就可以在界面中找到这些东西,如果是以前PC上将的windows操作系统,多半的东西都需要我们得新再装。

3、把软件内核或应用文件系统等东西烧到嵌入式系统硬件平台中的ROM中就实现了一个真正的“嵌入”。

以上的定义是我在6、7年前给嵌入式系统下自话侧重于理解型的定义,书上的定义也有很多,但在“嵌入式”这个领域范围内,谁都不敢说自己的定义是十分确切的,包括那些专家学者们,历为毕竟嵌入式系统是计算机范畴下的一门综合性学科。

二、如何学习嵌入式系统- -嵌入式系统的分层与专业的分类。

嵌入式系统分为4层,硬件层、驱动层、操作系统层和应用层。

1、硬件层,是整个嵌入式系统的根本,如果现在单片机及接口这块很熟悉,并且能用C和汇编语言来编程的话,从嵌入式系统的硬件层走起来相对容易,硬件层也是驱动层的基础,一个优秀的驱动工程师是要能够看懂硬件的电路图和自行完成CPLD的逻辑设计的,同时还要对操作系统内核及其调度性相当的熟悉的。但硬件平台是基础,增值还要靠软件。

硬件层比较适合于,电子、通信、自动化、机电一体、信息工程类专业的人来搞,需要掌握的专业基础知识有,单片机原理及接口_技术、微机原理及接口_技术、C语言。

2、 驱动层,这部分比较难,驱动工程师不仅要能看懂电路图还要能对操作系统内核十分的精通,以便其所写的驱动程序在系统调用时,不会独占操作系统时间片,而导 至其它任务不能动行,不懂操作系统内核架构和实时调度性,没有良好的驱动编写风格,按大多数书上所说添加的驱动的方式,很多人都能做到,但可能连个初级的 驱动工程师的水平都达不到,这样所写的驱动在应用调用时就如同windows下我们打开一个程序运行后,再打开一个程序时,要不就是中断以前的程序,要不 就是等上一会才能运行后来打开的程序。想做个好的驱动人员没有三、四年功底,操作系统内核不研究上几编,不是太容易成功的,但其工资在嵌入式系统四层中可 是最高的。

嵌入式的驱动层比较适合于电子、通信、自动化、机电一体、信息工程类专业尤其是计算机偏体系结构类专业的人来搞,除硬件层所具备的基础学科外,还要对数据结构与算法、操作系统原理、编译原理都要十分精通了解。

3、操作系统层,对于操作系统层目前可能只能说是简单的移植,而很少有人来自已写操作系统,或者写出缺胳膊少腿的操作系统来,这部分工作大都由驱动工程师来完成。操作系统是负责系统任务的调试、磁盘和文件的管理,而嵌入式系统的实时性十分重要。据说,XP操作系统是微软投入300人用两年时间才搞定的,总时工时是600人年,中科院软件所自己的女娲Hopen操作系统估计也得花遇几百人年才能搞定。因此这部分工作相对来讲没有太大意义。

4、应用层,相对来讲较为容易的,如果会在windows下如何进行编程接口函数调用,到操作系统下只是编译和开发环 境有相应的变化而已。如果涉及Jave方面的编程也是如此的。嵌入式系统中涉及算法的由专业算法的人来处理的,不必归结到嵌入式系统范畴内。但如果涉及嵌入式系统下面嵌入式数据库、基于嵌入式系统的网络编程和基于某此应用层面的协议应用开发(比如基于SIP、H.323、Astrisk)方面又较为复杂, 并且有难度了。

三、如何学习嵌入式系统- -目标与定位。

先有目标,再去定位。学ARM,从硬件上讲,一方面就是学习接口电路设计,另一方面就是学习汇编和C语言的板级编程。如果从软件上讲,就是要学习基于ARM处理器的操作系统层面的驱动、移植了。这些对于初学都来说必须明确,要么从硬件着手开始学,要么从操作系统的熟悉到应用开始学,但不管学什么,只要不是纯的操作系统级以上基于API的应用层的编程,硬件的寄存器类的东西还是要能看懂的,基于板级的汇编和C编程还是要会的。因此针对于嵌入式系统的硬件层和驱动程的人,ARM的接口电路设计、ARM的C语言和汇编语言编程及调试开发环境还是需要掌握的。

因此对于初学者必然要把握住方向,自己学习嵌入式系统的目标是什么,自己要在那一层面上走。然后再着手学习较好,与ARM相关的嵌入式系统的较为实际的两个层面硬件层和驱动层,不管学好了那一层都会很有前途的。

如果想从嵌入式系统的应用层面的走的话,可能与ARM及其它体系相去较远,要着重研究基嵌入式操作系统的环境应用与相应开发工具链,比如WinCe操作系统下的EVC应用开发(与windows下的VC相类似),如果想再有突破就往某些音视频类的协议上靠,比如VOIP领域的基于SIP或H.323协议的应用层开发,或是基于嵌入式网络数据库的开发等等。

对于初学者来讲,要量力而行,不要认为驱动层工资高就把它当成方向了,要结合自身特点,嵌入式系统四个层面上那个层面上来讲都是有高人存在,当然高人也对应的高工资,我是做硬件层的,以前每月工资中个人所得税要被扣上近3千大元,当然我一方面充当工程师的角色,一方面充当主管及人物的角色,两个职位我一个人干,但上班时间就那些。硬件这方面上可能与我PK的人很少了,才让我拿到那么多的工资。

四、如何学习嵌入式系统--开发系统选择。

很多ARM初学者都希望有一套自己能用的系统,但他们住住会产生一种错误认识就是认为处理器版本越高、性能越高越好,就象很多人认为ARM9与ARM7好,我想对于初学者在此方面以此入门还应该理智,开发系统的选择最终要看自己往嵌入式系统的那个方向上走,是做驱动开发还是应用,还是做嵌入式系统硬件层设计与板级测试。如果想从操作系统层面或应用层面上走,不管是驱动还是应用,那当然处理器性能越高越好了,但嵌入式系统这个东西自学,有十分大的困难,不是几个月或半年或是一年二年能搞定的事。

在某种意义上请,ARM7与9的差别就是在某些功能指令集上丰富了些,主频提高一些而已,就比如286和386。对于用户来讲可能觉查不到什么,只能是感觉速度有些快而已。

ARM7比较适合于那些想从硬件层面上走的人,因为ARM7系列处理器内部带MMU的很少,而且比较好控制,就比如S3C44B0来讲,可以很容易将Cache关了,而且内部接口寄存器很容易看明白,各种接口对于用硬件程序控制或AXD单步命令行指令都可以控制起来,基于51单片机的思想很容易能把他搞懂,就当成个32位的单片机,从而消除很多51工程师想转为嵌入式系统硬件ARM开发工程师的困惑,从而不会被业界某此不是真正懂嵌入式烂公司带到操作系统层面上去,让他们望而失畏,让业界更加缺少这方面的人才。

而嵌入式系统不管硬件设计还是软件驱动方面都是十分注重接口这部分的,选择平台还要考察一个处理器的外部资源,你接触外部资源越多,越熟悉他们那你以后就业成功的机率就越高,这就是招聘时所说的有无“相关技能”,因为一个人不可能在短短几年内把所有的处理器都接触一遍,而招聘单位所用的处理器就可能是我们完全没有见过的,就拿台湾数十家小公司(市价几千万)的公司生产的ARM类处理器,也很好用,但这些东西通用性太差,用这些处理器的公司就只能招有相关工作经验的人了,那什么是相关工作经验,在硬件上讲的是外围接口设计,在软件上讲是操作系统方面相关接口驱动及应用开发经验。

我从业近十年,2000年ARM出现,我一天始做ARM7,然后直接跑到了Xscale(这个板本在ARM10-11之间),一做就是五年,招人面试都不下数百人,在这些方面还是深有体会的。

我个人认为三星的S3C44b0对初学者来说比较合适,为什么这么说哪?因为接口资源比较丰富,技术成熟,资料较多,应该十分适合于初学者,有问题可能很容易找人帮且解决,因为大多数人都很熟悉,就如同51类的单片机,有N多位专家级的人物可以给你帮忙,相关问题得以很快解答,所然业界认为这款ARM都做用得烂了,但对于初学者来,就却是件好事。

因此开发系统的选择,要看自己的未来从来目标方向、要看开发板接口资源、还要看业界的通用性。

五、如何学习嵌入式系统--成为高级嵌入式系统硬件工程师要具备的技能。

对于硬件来讲有几个方向,就单纯信号来分为数字和模拟,模拟比较难搞,一般需要很长的经验积累,单单一个阻值或容值的精度不够就可能使信号偏差很大。因此年轻人搞的较少,随着技术的发展,出现了模拟电路数字化,比如手机的Modem射频模块,都采用成熟的套片,而当年国际上只有两家公司有此技术,自我感觉模拟功能不太强的人,不太适合搞这个,如果真能搞定到手机的射频模块,只要达到一般程度可能月薪都在15K以上。

另一类就是数字部分了,在大方向上又可分为51/ARM的单片机类,DSP类,FPGA类,国内FPGA的工程师大多是在IC设计公司从事IP核的前端验证,这部分不搞到门级,前途不太明朗,即使做个IC前端验证工程师,也要搞上几年才能胜任。DSP硬件接口比较定型,如果不向驱动或是算法上靠拢,前途也不会太大。而ARM单片机类的内容就较多,业界产品占用量大,应用人群广,因此就业空间极大,而硬件设计最体现水平和水准的就是接口设计这块,这是各个高级硬件工程师相互PK,判定水平高低的依据。

而接口设计这块最关键的是看时序,而不是简单的连接,比如PXA255处理器I2C要求速度在100Kbps,如果把一个I2C外围器件,最高还达不到100kbps的与它相接,必然要导致设计的失败。这样的情况有很多,比如51单片机可以在总线接LCD,但为什么这种LCD就不能挂在ARM的总线上,还有ARM7总线上可以外接个Winband的SD卡控制器,但为什么这种控制器接不到ARM9或是Xscale处理器上,这些都是问题。因此接口并不是一种简单的连接,要看时序,要看参数。

一个优秀的硬件工程师应该能够在没有参考方案的前提下设计出一个在成本和性能上更加优秀的产品,靠现有的方案,也要进行适当的可行性裁剪,但不是胡乱的来,我遇到一个工程师把方案中的5V变1.8V的DC芯片,直接更换成LDO,有时就会把CPU烧上几个。前几天还有人希望我帮忙把他们以前基于PXA255平台的手持GPS设备做下程序优化,我问了一下情况,地图是存在SD卡中的,而SD卡与PXA255的MMC控制器间采用的SPI接口,因此导致地图读取速度十分的慢,这种情况是设计中严重的缺陷,而不是程序的问题,因此我提了几条建议,让他们更新试下再说。因此想成为一个优秀的工程师,需要对系统整体性的把握和对已有电路的理解,换句话说,给你一套电路图你 终究能看明白多少,看不明白80%以上的话,说明你离优秀的工程师还差得远哪。

其次是电路的调试能力和审图能力,但最最基本的能力还是原理图设计PCB绘制,逻辑设计这块。这是指的硬件设计工程师,从上面的硬件设计工程师中还可以分出ECAD工程师,就是专业的画PCB板的工程师,和EMC设计工程师,帮人家解决EMC的问题。硬件工程师再往上就是板级测试工程师,就是C语功底很好的硬件工程师,在电路板调试过程中能通过自已编写的测试程序对硬件功能进行验证。然后再交给基于操作系统级的驱动开发人员。

总之,硬件的内容很多很杂,硬件那方面练成了都会成为一个高手,我时常会给人家做下方案评估,很多高级硬件工程师设计的东西,经常被我一句话否定,因此工程师做到我这种地步,也会得罪些人,但硬件的确会有很多不为人知的东西,让很多高级硬件工程师也摸不到头脑。

那么高级硬件件工程师技术技能都要具备那些东西哪,首先要掌握EDA设计的辅助工具类如ProtelORCADPowperPCBMaplux2ISE、VDHL语言,要能用到这些工具画图画板做逻辑设计,再有就是接口设计审图能力,再者就是调试能力,如果能走到总体方案设计这块,那就基本上快成为资深工程师了。

本文转载自:电子发烧友论坛
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

围观 6

车轮位置的确定是在制作小车的过程中必不可少的部件,好在STM32中包含了硬件的编码器。但使用的过程中却存在诸多不方便。下面由我一一道来:

1、编码器原理

什么是正交?如果两个信号相位相差90度,则这两个信号称为正交。由于两个信号相差90度,因此可以根据两个信号哪个先哪个后来判断方向、根据每个信号脉冲数量的多少及整个编码轮的周长就可以算出当前行走的距离、如果再加上定时器的话还可以计算出速度。

2、为什么要用编码器

“”

从上图可以看出,由于TI,T2一前一后有个90度的相位差,所以当出现这个相位差时就表示轮子旋转了一个角度。但有人会问了:既然都是脉冲,为什么不用普通IO中断?实际上如果是轮子一直正常旋转当然没有问题。仔细观察上图,如果出现了毛刺呢?这就是需要我们在软件中编写算法进行改正。于是,我们就会想到如果有个硬件能够处理这种情况那不是挺好吗?

3、STM32编码器

“”

还是刚才那张图,但这时候我们看到STM32的硬件编码器还是很智能的,当T1,T2脉冲是连续产生的时候计数器加一或减一一次,而当某个接口产生了毛刺或抖动,则计数器计数不变,也就是说该接口能够容许抖动。在STM32中,编码器使用的是定时器接口,通过数据手册可知,定时器1,2,3,4,5和8有编码器的功能,而其他没有。编码器输入信号TI1,TI2经过输入滤波,边沿检测产生TI1FP1,TI2FP2接到编码器模块,通过配置编码器的工作模式,即可以对编码器进行正向/反向计数。如果用的是定时器3,则对应的引脚是在PA6和PA7上。根据stmn32手册上编码器模式的说明,有6中组合计数方式,见下表。

“”

由此可知,通过选择可以确定使用定时器的哪种方式来得到我们所要的结果。STM32编码器的使用也非常简单,其基本步骤和开发STM32其他部件的操作一致,都是打开时钟,配置接口,配置模式,如果要用中断则打开中断。具体可以参考以下代码(这里使用的是TIM4,引脚采用GPIOA 11和GPIOA12):

bool EncodeInit(u8 none1,u32 period)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  EXTI_InitTypeDef   EXTI_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//??TIM3??
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//??GPIOA??
    
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
    
   GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_10);
  GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_10);
  
   TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // No prescaling 
  TIM_TimeBaseStructure.TIM_Period = 1333; 
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
  
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 10;
    TIM_ICInit(TIM4, &TIM_ICInitStructure);
  
  // Clear all pending interrupts
  TIM_ClearFlag(TIM4, TIM_FLAG_Update);
  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);

//Reset counter
  TIM_SetCounter(TIM4,0);
  TIM_Cmd(TIM4, ENABLE); 
    
     return 0;
}

4、编码器的中断

由于编码器是基于定时器的,所以编码器的中断实际上就是定时器的中断啦。也就是说定时器是每隔一定时间加一个数(或减一个数 ),当数到达预设值时就产生中断,而编码器是每一个有效脉冲就加一个数(或减一个数 ),当数到达预设值时就产生中断。若预设值为1000则编码器与定时器中断不同的是,当编码器反转时值到达999产生一次中断,而当编码器正转到达0时同样产生一次中断。在硬件上这两个中断是没法区分的,这也就造成了有种情况的误判。

5、STM32编码器没有考虑的情况

想象一下,如果编码器的预设值为1000,当某次我们使得编码器正转产生中断后,立即反转则又该怎么办呢?根据上面的说法,这时候会产生两次一样的中断。如果在算法上没有处理的话,极有可能认为是行走了两次正向。但实际上并没有。所以这个时候必须结合方向来判断行走的情况(判断方向使用的是DIR寄存器位)或者在产生中断后读一次count寄存器位(看看是999还是0,以此来判断当前的方向)。只有上一次为正且这一次同样为正,距离才是相加的。
具体中断处理函数代码如下:

void TIM4_IRQHandler(void)
{
    temp=(TIM_GetCounter(TIM4)&0xffff);    
if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
  {    
        if(temp==9999)
            {    
              count--;
                if(predir==0)//只有当前一次是负向走,这一次还是负向走才上传数据
                {
                    upcount--;
                
                }else{
                    predir=0;//表示往负向走      
              }
            }else if(temp==0)
            {
              count++;
                if(predir==1)//只有当前一次是正向走,这次又是正向走才上传数据
                {
                    upcount++;
                }else{
                    predir=1;//表示往正向走
                }
            }
            TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
  }else{
        #ifdef DEBUG
         printf("ENCODE TIMER INTERRUP ERROR! n");
    #endif
        while(1)
        {
            ;
        }

6、最后一个问题

那么如果当前并没有到一个中断怎么办?难道这个时候就不能得到编码器的精确位置了吗?

其实只是个非常简单的算法:

u32 EncodeGetMileage(u8 none1,u8 none2)
{  
      u8 i=0;
      temp=(TIM_GetCounter(TIM4)&0xffff);
                if(count<0)
                {
                     temp_mileage=(abs(count)-1)*1000 +(1000-temp);

                }else{
                    temp_mileage=count*1000 +temp;
                }
        } 
     
return temp_mileage; //返回编码器的脉冲个数,一个脉冲相当于125/1333 mm,这个返回值用于本次里程的计算,给总里程用。

每次把中断的次数记录下来,然后再把距离上次中断共走了多少个脉冲,再把两者相加即可。

本文转载自:电子工程世界论坛
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

围观 131

基于STM平台且满足实时控制要求操作系统,有以下5种可供移植选择,分别为μClinux、μC/OS-II、eCos、FreeRTOS和RT-thread。下面分别介绍这5种嵌入式操作系统的特点及不足,通过对比,读者可以根据自己的应用需求选择合适的平台。

TOP1:μClinux

μClinux是一种优秀的嵌入式Linux版本,其全称为micro-control Linux,从字面意思看是指微控制Linux。同标准的Linux相比,μClinux的内核非常小,但是它仍然继承了Linux操作系统的主要特性,包括良好的稳定性和移植性、强大的网络功能、出色的文件系统支持、标准丰富的API,以及TCP/IP网络协议等。因为没有MMU内存管理单元,所以其多任务的实现需要一定技巧。

μClinux在结构上继承了标准Linux的多任务实现方式,分为实时进程和普通进程,分别采用先来先服务和时间片轮转调度,仅针对中低档嵌入式CPU特点进行改良,且不支持内核抢占,实时性一般。

在内存管理上由于μClinux是针对没有MMU的处理器设计的,不能使用处理器的虚拟内存管理技术,只能采用实存储器管理策略。系统使用分页内存分配方式,在启动时对实际存储器进行分页。系统对内存的访问是直接的,操作系统对内存空间没有保护,多个进程可共享一个运行空间,所以,即使是一个无特权进程调用一个无效指针也会触发一个地址错误,并有可能引起程序崩溃甚至系统崩溃。

μClinux操作系统的中断管理是将中断处理分为两部分:顶半处理和底半处理。在顶半处理中,必须关中断运行,且仅进行必要的、非常少、速度快的处理,其他处理交给底半处理;底半处理执行那些复杂、耗时的处理,而且接受中断。因为系统中存在有许多中断的底半处理,所以会引起系统中断处理的延时。

μClinux对文件系统支持良好,由于μClinux继承了Linux完善的文件系统性能,它支持ROMFS、NFS、ext2、MS-DOS、JFFS等文件系统。但一般采用ROMFS文件系统,这种文件系统相对于一般的文件系统(如ext2)占用更少的空间。但是ROMFS文件系统不支持动态擦写保存,对于系统需要动态保存的数据须采用虚拟RAM盘/JFFS的方法进行处理。

在对硬件的支持上,由于μClinux继承了Linux的大部分性能,所以至少需要512KB的RAM空间,lMB的ROM/Flash空间。

在μClinux的移植方面,μClinux是Linux针对嵌入式系统的一种改良,其结构比较复杂。移植μClinux,目标处理器除了需要修改与处理器相关的代码外,还需要足够容量的外部ROM和RAM。

μClinux最大特点在于针对无MMU处理器设计,这对于没有MMU功能的STM32F103来说是合适的,但移植此系统需要至少512KB的RAM空间,1MB的ROM/FLASH空间,而STM32F103拥有256K的FLASH,需要外接存储器,这就增加了硬件设计的成本。μClinux结构复杂,移植相对困难,内核也较大,其实时性也差一些,若开发的嵌入式产品注重文件系统和与网络应用则μClinux是一个不错的选择。

TOP2:μC/OS-II

μC/OS-II是在μC/OS的基础上发展起来的,是用C语言编写的一个结构小巧、抢占式的多任务实时内核。μC/OS-II能管理64个任务,并提供任务调度与管理、内存管理、任务间同步与通信、时间管理和中断服务等功能,具有执行效率高、占用空间小、实时性能优良和扩展性强等特点。

对于实时性的满足上,由于μC/OS-II内核是针对实时系统的要求设计实现的,所以只支持基于固定优先级抢占式调度;调度方法简单,可以满足较高的实时性要求。

在内存管理上,μC/OS-II把连续的大块内存按分区来管理,每个分区中都包含整数个大小相同的内存块,但不同分区之间内存的大小可以不同。用户动态分配内存时,只须选择一个适当的分区,按块来分配内存,释放时将该块放回到以前所属的分区,这样就消除了因多次动态分配和释放内存所引起的碎片问题。

μC/OS-II中断处理比较简单。一个中断向量上只能挂一个中断服务子程序ISR,而且用户代码必须都在ISR(中断服务程序)中完成。ISR需要做的事情越多,中断延时也就越长,内核所能支持的最大嵌套深度为255。

在文件系统的支持方面,由于μC/OS-II是面向中小型嵌入式系统的,即使包含全部功能,编译后内核也不到10 KB,所以系统本身并没有提供对文件系统的支持。但是μC/OS-II具有良好的扩展性能,如果需要也可自行加入文件系统的内容。

在对硬件的支持上,μC/OS-II能够支持当前流行的大部分CPU,μC/OS-II由于本身内核就很小,经过裁剪后的代码最小可以为2KB,所需的最小数据RAM空间为4 KB,μC/OS-II的移植相对比较简单,只需要修改与处理器相关的代码就可以。

μC/OS-II是一个结构简单、功能完备和实时性很强的嵌入式操作系统内核,针对于没有MMU功能的CPU,它是非常合适的。它需要很少的内核代码空间和数据存储空间,拥有良好的实时性,良好的可扩展性能,并且是开源的,网上拥有很多的资料和实例,所以很适合向STM32F103这款CPU上移植。

TOP3:eCos

eCos(embedded Configurable operating system),即嵌入式可配置操作系统。它是一个源代码开放的可配置、可移植、面向深度嵌入式应用的实时操作系统。最大特点是配置灵活,采用模块化设计,核心部分由小同的组件构成,包括内核、C语言库和底层运行包等。每个组件可提供大量的配置选项(实时内核也可作为可选配置),使用eCos提供的配置工具可以很方便地配置,并通过不同的配置使得eCos能够满足不同的嵌入式应用要求。

在实时性反面,由于eCos调度方法丰富,提供了两种基于优先级的调度器(即位图调度器和多级队列调度器),允许用户在进行配置时选择其中一个调度器,适应性好。因此在实时性方面表现良好。

在内存管理上eCos对内存分配既不分段也不分页,而是采用一种基于内存池的动态内存分配机制。通过两种内存池来实现两种内存管理方法:一种是变长的内存池;另一种是定长的内存池,类似于VxWorks的管理方案。

在中断管理上eCos使用了分层式中断处理机制,把中断处理分为传统的ISR(中断服务程序)和滞后中断服务程序DSR(递延服务程序)。类似于μClinux的处理机制,这种机制可以在中断允许时运行DSR,因此在处理较低优先级中断时允许高优先级的中断和处理。为了极大地缩短中断延时,ISR应当可以快速运行。如果中断引起的服务量少,则ISR可以单独处理中断;如果中断服务复杂,则ISR只屏蔽中断源,然后交由DSR(递延服务程序)处理。

eCos操作系统的可配置性非常强大,用户可以自己加入所需的文件系统。eCos操作系统同样支持当前流行的大部分嵌入式CPU,eCos操作系统可以在16位、32位和64位等不同体系结构之间移植。eCos由于本身内核就很小,经过裁剪后的代码最小可以为10 KB,所需的最小数据RAM空间为10 KB。

在系统移植方面 eCos操作系统的可移植性很好,要比μC/OS-II和μClinux容易。

eCos最大特点是配置灵活,并且支持无MMU的CPU的移植,开源且具有很好的移植性,也比较合适于移植到STM32平台的CPU上。但eCOS的应用还不是太广泛,还没有像μC/OS-II那样普遍,并且资料也没有μC/OS-II多。eCos适合用于一些商业级或工业级对成本敏感的嵌入式系统,例如消费电子领域中的一些应用。

TOP4:FreeRTOS

由于RTOS需占用一定的系统资源(尤其是RAM资源),只有μC/OS-II、embOS、salvo、FreeRTOS等少数实时操作系统能在小RAM单片机上运行。相对于μC/OS-II、 embOS等商业操作系统,FreeRTOS操作系统是完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为6.0版。

作为一个轻量级的操作系统,FreeRTOS提供的功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能等,可基本满足较小系统的需要。 FreeRTOS内核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优先级,CPU总是让处于就绪态的、优先级最高的任务先运行。 FreeRT0S内核同时支持轮换调度算法,系统允许不同的任务使用相同的优先级,在没有更高优先级任务就绪的情况下,同一优先级的任务共享CPU的使用 时间。

FreeRTOS的内核可根据用户需要设置为可剥夺型内核或不可剥夺型内核。当 FreeRTOS被设置为可剥夺型内核时,处于就绪态的高优先级任务能剥夺低优先级任务的CPU使用权,这样可保证系统满足实时性的要求;当 FreeRTOS被设置为不可剥夺型内核时,处于就绪态的高优先级任务只有等当前运行任务主动释放CPU的使用权后才能获得运行,这样可提高CPU的运行 效率。

FreeRTOS的移植:FreeRTOS操作系统可以被方便地移植到不同处理器上工作,现已提供了ARM、MSP430、 AVR、PIC、C8051F等多款处理器的移植。FrceRTOS在不同处理器上的移植类似于μC/OS-II,故本文不再详述FreeRTOS的移 植。此外,TCP/IP协议栈μIP已被移植到FreeRTOS上,具体代码可见FreeRTOS网站。

相对于常见的μC/OS-II操作系统,FreeRTOS操作系统既有优点也存在不足。其不足之处, 一方面体现在系统的服务功能上,如FreeRTOS只提供了消息队列和信号量的实现,无法以后进先出的顺序向消息队列发送消息;另一方 面,FreeRTOS只是一个操作系统内核,需外扩第三方的GUI(图形用户界面)、TCP/IP协议栈、FS(文件系统)等才能实现一个较复杂的系统, 不像μC/OS-II可以和μC/GUI、μC/FS、μC/TCP-IP等无缝结合。

TOP5:RT-thread

RT-Thread 是一款主要由中国开源社区主导开发的开源实时操作系统(许可证GPLv2)。实时线程操作系统不仅仅是一个单一的实时操作系统内核,它也是一个完整的应用系统,包含了实时、嵌入式系统相关的各个组件:TCP/IP协议栈,文件系统,libc接口,图形用户界面等。

中国人自己开发的,稳定版本是 1.2.1,有希望看完源码。精简、靠谱,自带一个叫做 finsh 的片上调试工具,非常实用。各种信号量、互斥锁、邮箱、事件等线程协同功能都有。

需要注意的是,RT-thread 2.0 版本的设计思想和 1.2 的完全不同,将会把 Linux 纳入进来,是的,不是在Linux 里面嵌入RT-thread,而是把Linux 嵌入到 RT-thread 里面!

RT-thread 的文档呢,官网是有的,不过,真的是只能作为参考,很明显是开发人员的事后开发笔记整理的。目前还是只能通过看代码来理解详细的使用方式,从文档和论坛的只言片语里面,是难以还原真相的。RT-thread 的好处就是它的版本还比较小,即便缺乏文档,也是可以看源码看下去的。

本文转载自:我爱方案网
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。

围观 40

在嵌入式产品中有时候需要实现对外部的模拟量进行采样处理和记录,而这就需要使用到ADC功能,将外部的模拟量转换成数字量。而在复杂的嵌入式产品中,往往需要使用多路AD采样,例如在智能家居产品,电池电量检测,热敏温度传感器,烟雾传感器,气敏传感器等都是可以使用ADC来实现采样的。在本文章,将会介绍如何通过意法的STM32 MCU实现用DMA完成多通道的AD采样功能。

什么叫ADC

ADC即模拟数字转换器(英语:Analog-to-digital converter)是用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。一个模拟数字转换器可以提供信号用于测量。与之相对的设备成为数字模拟转换器。

影响AD采样的因素有哪些

分辨率

分辨率指的是ADC的位数,例如STM32F103MCU的内部ADC的分辨率是12位,那么它所采样的结果就在0-4096之间。

最小采样单位值

根据基准电压和参考电压的不同,其值也是不同的,例如在基准电压为3.3V,参考电压最低为0V,最高为3.3V,采样分辨率位12位的嵌入式系统中,则ADC的最小量程单位则为:3.3V/4096 = 0.00080566。

量程

在无负电压的嵌入式系统中,量程范围0-基准电压。

电源噪音

电源质量直接影响了AD采样的正确性和稳定性,如果条件满足,建议使用线性稳压源,若是使用开关电源的话,需要在VDDA模拟电源输入和参考电压输入接一个线性稳压管,同时要注意减小PCB板布局走线中结电容对采样电路的影响。

STM32F103 ADC主要特性

  • 12-位分辨率
  • 转换结束,注入转换结束和发生模拟看门狗事件时产生中断
  • 单次和连续转换模式
  • 从通道0到通道n的自动扫描模式
  • 自校准
  • 带内嵌数据一致的数据对齐
  • 通道之间采样间隔可编程
  • 规则转换和注入转换均有外部触发选项
  • 间断模式
  • 双重模式(带2个或以上ADC的器件)
  • ADC转换时间:─ STM32F103xx 增强型产品: ADC 时钟为 56MHz 时为 1μs(ADC 时钟为 72MHz 为 1.17μs)─ STM32F101xx 基本型产品: ADC 时钟为 28MHz 时为 1μs(ADC 时钟为 36MHz 为 1.55μs)─ STM32F102xxUSB 型产品: ADC 时钟为 48MHz 时为 1.2μs
  • ADC供电要求: 2.4V到3.6V
  • ADC输入范围: VREF- ≤ VIN ≤ VREF+● 规则通道转换期间有DMA请求产生。

DMA简介

直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU任何干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

STM32F103 DMA主要特性

  • 12个 独立的可配置的通道(请求)DMA1有7个通道, DMA2有5个通道
  • 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
  • 在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推) 。
  • 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
  • 支持循环的缓冲器管理
  • 每个通道都有3个事件标志(DMA 半传输, DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
  • 存储器和存储器间的传输
  • 外设和存储器,存储器和外设的传输
  • 闪存、 SRAM、外设的SRAM、 APB1 APB2和AHB外设均可作为访问的源和目标。
  • 可编程的数据传输数目:最大为65536

如何实现多通道AD采样的DMA传输

“”

ADC功能引脚配置

void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    /* Enable DMA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    /* Enable ADC1 and GPIOC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC1_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC1_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC2_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC2_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC3_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC3_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率

    GPIO_InitStructure.GPIO_Pin = ADC4_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(ADC4_GPIOX,&GPIO_InitStructure);
    
}



配置多通道ADC功能

void ADC_Multichannel_Config(void)
{
    ADC_InitTypeDef  ADC_InitStructure; 
    
    ADC_DeInit(ADC1);  //将外设 ADC1 的全部寄存器重设为缺省值
    
    /* ADC1 configuration ------------------------------------------------------*/ 
    
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 
    ADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
    
    /* ADC1 regular channel11 configuration */ 
    //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
    //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5);
    // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)     
    ADC_DMACmd(ADC1, ENABLE); 
    /* Enable ADC1 */    
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
     /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器 
    
    /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器 
    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);  //开始指定ADC1的校准状态 
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1)); 
    //获取指定ADC1的校准程序,设置状态则等待
}



配置DMA通道,使能ADC转换结果从外设到内存

void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
    DMA_DeInit(DMA1_Channel1);   //将DMA的通道1寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;  //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //内存作为数据传输的目的地
    DMA_InitStructure.DMA_BufferSize = N*M;  //DMA通道的DMA缓存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据位宽度16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循环缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数DMA通道



开始启动ADC转换功能

void ADC_Start(void)
{
    ADC_GPIO_Config();
    ADC_Multichannel_Config();
    DMA_Configuration();
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //开始ADC转换
    DMA_Cmd(DMA1_Channel1, ENABLE);  //启动DMA通道
    
}



使用冒泡排序法,对各通道的连续五次采样结果取平均值

u16 ADC1_AveragValue(u16 ADC_Value[N][M])
{
    u16 ADC1_Value[N];
    u8 i = 0;
    u8 j = 0;
    u16 temp = 0;
    u16 ADC1_Av = 0;
    for (i = 0;i < N;i++)
    {
              ADC1_Value[i] = ADC_Value[i][0];
    }
    /*冒泡排序*/
    for(i=0; i<N-1; i++)
    {
//内循环选择要进行比较的数
        for(j=0; j<N-1-i; j++)
        {
             if(ADC1_Value[j]>ADC1_Value[j+1])
             {
                    temp=ADC1_Value[j];
                    ADC1_Value[j]=ADC1_Value[j+1];
                    ADC1_Value[j+1]=temp;
             }
        }
    }
    /*去掉最大值和最小值*/
    for (i = 0; i<N-2;i++)
    {
        ADC1_Av += ADC1_Value[i+1];
        
    }
    /*取平均值*/
    ADC1_Av = ADC1_Av/4;
    return ADC1_Av;
    
}



转换采样结果

(float)ADC1_AveragValue(ADC_Value)/4096*3.3)



本文转载自:
简书作者:桂慧要努力当个攻城师
转载地址:https://www.jianshu.com/p/7ee23bb2cb65
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编进行处理。

围观 60

一:综述

STM32 目前支持的中断共为 84 个(16 个内核+68 个外部), 16 级可编程中断优先级 的设置(仅使用中断优先级设置 8bit 中的高 4 位)和16个抢占优先级(因为抢占优先级最多可以有四位数)。

二:优先级判断

STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作‘亚优先级’或‘副优先级’,每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

三:优先级分组

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位在NVIC应用中断与复位控制寄丛器(AIRCR)的中断优先级分组域中,可以有8种分配方式,如下:

所有8位用于指定响应优先级

最高1位用于指定抢占式优先级,最低7位用于指定响应优先级

最高2位用于指定抢占式优先级,最低6位用于指定响应优先级

最高3位用于指定抢占式优先级,最低5位用于指定响应优先级

最高4位用于指定抢占式优先级,最低4位用于指定响应优先级

最高5位用于指定抢占式优先级,最低3位用于指定响应优先级

最高6位用于指定抢占式优先级,最低2位用于指定响应优先级

最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。

Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位(AIRCR高四位),这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级

第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级

第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级

第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级

第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组

NVIC_PriorityGroup_1 => 选择第1组

NVIC_PriorityGroup_2 => 选择第2组

NVIC_PriorityGroup_3 => 选择第3组

NVIC_PriorityGroup_4 => 选择第4组

中断优先级分组是为了给抢占式优先级和响应优先级在中断优先级寄丛器的高四位分配各个优先级数字所占的位数。在一个程序中只能设定一次。

四:中断源的优先级

接下来就是指定中断源的优先级,中断源优先级是在中断优先级寄存器中设置的,只能设置及高四位,必须根据中断优先级分组中设置好的位数来在该寄存器中设置相应的数值。假如你选择中断优先级分组的第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级,那么抢占式优先级就有000-111共八种数据选择,也就是有八个中断嵌套,而响应优先级中有0和1两种,总共有8*2=16种优先级。

中断源优先级具体的设置了该中断源的优先级别

在一个程序中可以设定多个(最多16个)优先级,每个中断源只能设定的一个。

每写一个关于中断优先级的程序必须包含下列两个函数:

(1)void NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup)中断分组设置

(2)void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)中断优先级设置

具体设置:

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组

NVIC_PriorityGroup_1 => 选择第1组

NVIC_PriorityGroup_2 => 选择第2组

NVIC_PriorityGroup_3 => 选择第3组

NVIC_PriorityGroup_4 => 选择第4组

五:举例

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

// 使能EXTI0中断

NVIC_InitStructure.NVIC_IRQChannel =EXTI0_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreempTIonPriority = 1; //指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //指定响应优先级别0

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

// 使能EXTI9_5中断

NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //指定抢占式优先级别0

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定响应优先级别1

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

六:注意事项

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

七:开关总中断

在STM32/Cortex-M3中是通过改变CPU的当前优先级来允许或禁止中断。

PRIMASK位:只允许NMI和hardfault异常,其他中断/异常都被屏蔽(当前CPU优先级=0)。

FAULTMASK位:只允许NMI,其他所有中断/异常都被屏蔽(当前CPU优先级=-1)。

在STM32固件库中(stm32f10x_nvic.c和stm32f10x_nvic.h)定义了四个函数操作PRIMASK位和FAULTMASK位,改变CPU的当前优先级,从而达到控制所有中断的目的。

下面两个函数等效于关闭总中断:

void NVIC_SETPRIMASK(void);

void NVIC_SETFAULTMASK(void);

下面两个函数等效于开放总中断:

void NVIC_RESETPRIMASK(void);

void NVIC_RESETFAULTMASK(void);

上面两组函数要成对使用,不能交叉使用。

例如:

第一种方法:

NVIC_SETPRIMASK(); //关闭总中断

NVIC_RESETPRIMASK(); //开放总中断

第二种方法:

NVIC_SETFAULTMASK(); //关闭总中断

NVIC_RESETFAULTMASK(); //开放总中断

常常使用

NVIC_SETPRIMASK(); // Disable Interrupts

NVIC_RESETPRIMASK(); // EnableInterrupts

补充 可以用

#define CLI() __set_PRIMASK(1)

#define SEI() __set_PRIMASK(0)

stm32优先级

1.优先级等级:

STM32用户能分配的优先级有16级,也就是用优先级寄存器NVIC->IP[x]的高四位来表示莫个中断的优先级。

2.优先级组:

在STM32中将一个中断的优先级分为:抢占优先级和子优先级。

在进行优先级判断的时候先是比较抢占优先级然后比较子优先级。

在固件库中用变量分别表示抢占优先级和子优先级:

NVIC_InitTypeDef.NVIC_IRQChannelPreemptionPriority;(抢占优先级)

NVIC_InitTypeDef.NVIC_IRQChannelSubPriority;(子优先级)

优先级组就是对抢占优先级和子优先级进行的分界:

例如设置优先级组为0x05,那么表示的是莫个中断的优先级从第5位开始为界限,高两位[6:7]是抢占优先

级。第两位是[5:4]表示的是子优先级。

举例说明:

//调用优先级组设置函数,设置优先级是0x05.

.....

NVIC_SetPriorityGrouping(5);

.....

//这里说明了一个优先级寄存器的(NVIC->IP[x])的7,6位表示的是抢占优先级。5,4表示的是优先级。

......

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

.......

//这里进行优先级的设置:通过上面的设置可以知道抢占优先级和子优先级的范围都是3~0;

//那么这样就设置好了一个中断的优先级

//====================================================

//

STM32 中断与嵌套NVIC 快速入门。

// netjob 2008-8-1

//====================================================

我也是靠看这本书才弄懂的:

Cortex-M3 权威指南

Joseph Yiu 著

宋岩 译

其实很简单。

//CM3 有 最多240个中断(通常外部中断写作IRQs),就是 软件上说的IRQ_CHANAELx(中断通道号x)

每个中断有自己的可编程的中断优先级【 有唯一对应的 中断优先级寄存器 】。

由于CM3支持 硬件中断嵌套,所以可以有 256 级的可编程优先级和 256级中断嵌套【书上称:抢占(preempt)优先级】

所以大家可以设:

IRQ CHANAEL 0 通道 = 2 中断优先级

WWDG 窗口定时器中断

IRQ CHANAEL 1 通道 = 0 中断优先级

PVD

联到EXTI的电源电压检测(PVD)中断

IRQ CHANAEL 3 通道 = 255 中断优先级

RTC 实时时钟(RTC)全局中断

IRQ CHANAEL 6 通道 = 10 中断优先级

EXTI0 EXTI线0中断.....

IRQ CHANAEL 239 通道 = (0

这个实在是太恐怖了! 是的,其实CM3 并没有这样做。

实在的芯片例如STM32等就只有设计来可用才64级可编程优先级和8级中断嵌套。

对 64级中断就是说:( INT0 到INT63)这个大家比较好理解,其它的64···239就不用了。

IRQ CHANAEL 0 。。。 IRQ CHANAEL 63

而8级中断嵌套这又是何解呢? 是这样的,上面说 一个 【中断】对应一个【中断优先级寄存器】,而这个寄存器是 8 位的。 当然就是256级了。而现在就用了 它其中的 BIT7,IT6,BIT5

三位来表示,而且是MSB对齐的。 用了3 个位来表达优先级(MSB 对齐的我们能够使用的8个优先级为:0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)

这样我们在【中断优先级寄存器】就不能按理论的填0到255之间的数了,而只能填0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)

所以大家可以设:

IRQ CHANAEL 0 通道 = 0x20 中断优先级

WWDG 窗口定时器中断

IRQ CHANAEL 1 通道 = 0x40 中断优先级

PVD

联到EXTI的电源电压检测(PVD)中断

IRQ CHANAEL 3 通道 = 0x20 中断优先级

RTC 实时时钟(RTC)全局中断

IRQ CHANAEL 6 通道 = 0xA0 中断优先级

EXTI0 EXTI线0中断

.....

IRQ CHANAEL 63 通道 =【0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0

以及0xE0。)】

大家注意到了,上面通道0和通道3 的优先级都是0X20, 这怎么办?

//

如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。如IRQ#0会比IRQ #3先得到响应,而且文中还讲了【优先级分组】,这又是什么回事?其实我回头看来,这个【优先级分组】和【抢占优先级】【亚优先级】都毫无意义的。

如果当时用 256级即是把【中断优先级寄存器】的8位都全用上,就没这个必要了。还什么优先级分组呢!

就是因为厂家现在【偷工减料】,才搞出这个明堂来的。

是这样的,在应用程序中断及复位控制寄存器(AIRCR) 中的10:8位【3位】是表示【优先级分组】它作用主要是用于对【中断优先级寄存器】『我们现在中用了BIT7,BIT6,BIT5三位』的功能的说明。

有一个表,在《Cortex-M3 权威指南》的110页,例如我们把AIRCR的10:8位设为【5】,查表可得【抢占优先级】=【7:6】,【亚优先级】=【5:0】,对于【中断优先级寄存器】只用了BIT7,6,5,因此我们可以看作是【7:6】,【5】。那4-0 可以不管。

现在我们的 IRQ0=0X20, IRQ3=0X20, 也就是 【0 0 1 0】『bit7=0,bit6=0,bit5=1,bit4=0』因为大家(IRQ0/IRQ3)的【抢占优先级】=【7:6】都是0,说明它们的中断相应级别是一样的。再继续判断它们哪个更优先的责任就要看【5】,结果连【5】都是一样的!

那就按默认:

// 如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。如IRQ#0会比IRQ #3先得到响应,由于CM3没有进中断【关全局中断相应】这事,只要是中断通道打开了,就会存在通道间的嵌套,即是会发生【抢占】的情况了。

上面就简短的说明,如果要详细理解,可以看《Cortex-M3权威指南》。有任何理解不当,请各位多多指教!

补充注意:

“2)抢占式优先级别相同的中断源之间没有嵌套关系;”

所以大家可以设:

IRQ CHANAEL 0 通道 = 0x20 中断优先级 WWDG 窗口定时器中断

IRQ CHANAEL 1 通道 = 0x40 中断优先级 PVD

联到EXTI的电源电压检测(PVD)中断

IRQ CHANAEL 3 通道 = 0x20 中断优先级 RTC 实时时钟(RTC)全局中断

IRQ CHANAEL 6 通道 = 0xA0 中断优先级 EXTI0EXTI线0中断

这样 0 通道和3 通道就不会有嵌套情况,而是0 通道按默认比3 通道优先高些。而0 通道与1通道就会有嵌套情况。

芯片复位后,默认的优先级分组 是 0, 就是 【7:1】表示抢占式优先级,【0】表示亚优先级, 这样对于MSB对齐的 8个优先级为:0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)

使用就很方便了,建议大家就用默认的默认的优先级分组是0,也就是复位后的值,哈哈!

例如下面的两个按键,都使用外中断方式,使用了PD.3,和PD.4两个引脚。这两个中断的优先级都是0X20,按默认的优先级分组,它们之间不会发生中断嵌套。

STM32_Nvic_Regs->Priority[9].all=0x20; // 中断的优先级是0X20

STM32_Nvic_Regs->Enable[0].bit.INT9=1; // 开INT9 中断IRQ9

STM32_Nvic_Regs->Priority[10].all=0x20; // 中断的优先级是0X20

STM32_Nvic_Regs->Enable[0].bit.INT10=1; // 开INT10 中断IRQ10

本文转载自:jesliebest的博客
转载地址:http://blog.csdn.net/u013538360/article/details/24740771
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编进行处理。

围观 45

页面