1. S5PV210 PWM模块解析
1)结构概述
A. 时钟选择
① PCLK_PSYS
② SCLK_PWM
使用二者的区别在于,PCLK_PSYS需要经过两级分频器,得到需要的频率;而SCLK_PWM不经过第一级分频器,在第二级分频器中为单独的选项,因此不会对SCLK_PWM进行分频操作
在使用的观念上,SCLK_PWM相当于将分频步骤在CMU模块中完成,输出给PWM模块时直接是目标时钟
B. 两级分频
① 一级预分频(8位整数分频器)
Timer 0 & 1共享一个预分频器;Timer 2 & 3 & 4共享一个预分频器
预分频系数:1 ~ 255
② 二级分频(固定比例分频器)
每个Timer各有一个二级分频器
二级分频器设置为可选的固定分频系数(1/1、1/2、1/4、1/8、1/16)
最终的分频公式如下:
在使用66MHz PCLK_PSYSY的情况下,结合两级分频器和32位计数器,定时范围如下:
C. 输出特征
Timer 0 ~ 3具有PWM功能和外部引脚,可以驱动外部IO信号;同时可以触发SoC内部中断
Timer 4没有PWM功能(没有TCMPB寄存器)和外部引脚,因此只能作为内部定时器使用
Timer 0配有死区生成器,可以在Timer 0 & 1产生死区信号,用于驱动大电流设备
2)工作模式
A. 工作流程
① 事先将计算好的计数值写入TCNTB,在启动timer前将TCNTB的值装载到TCNT寄存器中(首次启动计数时,需要手动装载计数值),装载完成后就可以启动timer
② 如果要启用PWM功能(即输出PWM波形),则同时还需要设置TCMPB寄存器。该寄存器默认值为0,故不会实现电平反转
③ 计时开始后,TCNT在模块时钟的驱动下递减,当递减到TCNT == TCMP时,电平值反转
当TCNT递减到0时,电平再次反转。如果使能中断,则PWM模块发出中断请求;如果使能auto-reload,则再次将TCNTB & TCMPB中的值加载到TCNT和TCMP中,并开始新一轮计数
④ 当前TCNT的值可以通过TCNTO寄存器观察
B. TCNT & TCMP、TCNTB & TCMPB与双缓冲特性
① TCNTB & TCMPB为有地址的寄存器,供程序员操作;TCNT & TCMP为内部递减计数和比较用的寄存器,没有寄存器地址,程序员不能编程访问该寄存器
② 两组计数 & 比较寄存器构成了PWM的双缓冲特性
所谓双缓冲特性就是在不停止当前计数的情况下,修改下次的计数值。如果使能auto-reload,并且在程序中修改了TCNTB & TCMPB寄存器的值,将在下个周期生效。
特别注意:如果要设置下个周期的计数值,在修改TCNTB & TCMPB寄存器的值之后,auto-reload的值应为1,manaul-update的值应为0。
如果此时设置manual-update的值为1,TCNTB & TCMPB的值会被立即加载到TCNT & TCMP中
3)启动PWM定时器步骤
① 将计数/比较的初始值写入TCNTB & TCMPB
② 将manual-update位置1,然后置0(手动装载)
③ 将start-bit位置1,启动计时器
4)PWM波形
PWM波形有2个重要参数:
① 周期T
② 占空比(duty ratio)
其中周期由TCNTB寄存器控制,假设PWM模块时钟频率配置为FIN,则周期为:
TCNTB / FIN(这也是定时时间)
即PWM波形的频率 fre = FIN / TCNTB
占空比则由TCMPB寄存器控制,假设波形如上图所示,开始计数时PWM输出低电平,TCNT == TCMP时输出高电平,那么占空比为TCMPB / TCNTB
但是S5PV210的PWM起始电平是可设置的,如果输出极性与上图相反,即开始计数时输出高电平,TCNT == TCMP时输出低电平,那么占空比为
(TCNTB - TCMPB) / TCNTB = 1 - TCMPB / TCNTB
5)输出电平控制
S5PV210的PWM输出极性可由inverter-on/off位配置,具体如下图所示
特别注意:inverter-bit对定时停止时PWM输出电平的影响
A. 正常计时结束
Inverter off:高电平
Inverter on:低电平
B. 强制停止计时
首先,可以通过将start-bit置0强制停止计时,此时PWM波形的输出极性与TCNT和TCMP的值有关,具体如下,
Inverter off:
TCNT > TCMP:低电平
TCNT <= TCMP:高电平
Inverter on:
TCNT > TCMP:高电平
TCNT <= TCMP:低电平
之所以讨论这个问题,是在用PWM控制蜂鸣器时要确保未计时和计时停止时蜂鸣器不工作(即PWM输出为低电平)~~
6)死区功能
应用场景:PWM有一个应用就是在功率电路中用来对交流电压进行整流,整流时2路分别在高电平和低电平时导通工作,不能同时导通。
如用PWM的输出信号及其反相信号控制2个开关,加入死区功能后,就可以避免2个开关同时导通
注意:出于功能的正确性,死区长度需要小于compare counter value
2. S5PV210 PWM程序设计
1)蜂鸣器工作原理
使用Timer 2输出的PWM电压信号来驱动蜂鸣器工作,其中蜂鸣器的频率可以通过设置PWM波形的频率进行设置。考虑到一般引脚的驱动能力不足,蜂鸣器会额外使用三极管来供电。
在本示例中,当PWMTOUT2输出高电平时蜂鸣器开始工作。因此需要确保PWM在未开始计时工作时必须处于低电平,在计时结束后也必须处于低电平
2)核心寄存器解析
A. TCFG0 & TCFG1
TCFG0 & TCFG1寄存器用于设置两级分频系数,如使用SCLK_PWM作为模块时钟,则该时钟不会被分频
注意:根据芯片手册,使用SCLK_PWM作为时钟时,占空比会有微小错误,减小该错误的方式是确保SCLK_PWM频率低于PCLK_PSYS(SCLK_PWM的建议值为低于1MHz)
B. TCON
说明1:启动定时器时涉及manual-update & start-bit位
先将计数值写入TCNTB & TCMPB,然后将manual-update位置1再置0,如此便实现将初始计数值写入TCNT & TCMP。最后将start-bit置1,便可启动计时
说明2:注意对inverter-bit的设置
根据蜂鸣器的工作原理,要确保未计时工作时PWM输出低电平,计时完成后也要保持低电平。结合interter-bit与PWM输出电平的对应关系,在初始化时需要设置该位
C. TCNTBn & TCMPBn & TCNTOn
这三个寄存器均为32位,用于实现对计数值的设置
D. TINT_CSTAT
此处中断使能和中断状态共用一个寄存器,在PWM的ISR中需要清pend
3)PWM控制蜂鸣器代码解析
A. PWM初始化代码解析
说明1:首先要将Timer 2对应的GPIO管脚设置位TOUT_2功能
说明2:设置两级分频
之前设置的PCLK_PSYS为66MHz,此处将一级分频系数设为65,二级分频系数设为1/1,所以最终Timer 2频率为:
66 / (65 + 1) / 1 = 1MHz
B. 蜂鸣器频率设置代码解析
说明:使用除法引入的编译问题
由于set_frequency函数中使用了除法,需要相关库的支持,因此编译时会有如下报错
解决方案:在链接时增加对libgcc.a的链接
可见在增加对libgcc.a的链接后,对__aeabi_uidiv的引用问题已经解决,但是在__aeabi_idiv0函数中出现对raise函数的引用问题
根据网上资料,在代码中按如下方式添加raise函数,编译链接可成功
注意:根据网上资料,该函数在uboot源代码的arch/arm/lib/eabi_compat.c中,该文件为符合eabi接口的工具链提供一些通用函数。
但是在我目前的210 uboot中已经没有该函数与该文件~~
根据之前的分析,是arm交叉工具链中的数学函数调用了raise函数,用于处理除法错误,调用过程如下
除法 --> __aeabi_uidiv --> __aeabi_idiv0 --> raise
参考资料:
C. 启动 & 停止蜂鸣器代码解析
此处启动定时器比较简单,关键是如何停止寄存器。如果直接将start-bit位清零,虽然定时器会停止工作,但是PWM的输出电平可能为高电平,此时蜂鸣器仍在工作。因此要确保定时器停止后,PWM输出低电平。
上例中采用的方式是将auto-reload位清零,如此Timer在结束本周期的计数后,将不再加载缓冲寄存器中的值并停止工作(当然,还要配合invert-bit的设置~~)
4)基于定时器中断的按键去抖动实现
A. 目标与原理
目标:由于机械按键的工作特性,一次按下可能触发多次外部中断。可以配合定时器,消除按键抖动对按键次数判断的影响
原理:
① 初始化按键触发外部中断,PWM Timer也使能中断
② 在按键的ISR中更新Timer计数值(stop --> config --> start)
③ 按键的抖动会不断重置计数器,当抖动结束时,Timer计时结束,在Timer的ISR中标识按键按下
B. Timer初始化代码分析
由于timer 4没有PWM功能也没有外部引脚,专门用于内部计时器,因此此处使用该计时器实现去抖动。
在去抖动的应用场景下,并不需要自动装载,因此保留其初始值0
C. 按键ISR代码分析
在按键ISR中并不标识按键按下,只是重置定时器。当按键抖动结束后,不会再重置定时器,当定时器time out后将调用Timer的ISR
D. Timer ISR代码分析
在Timer的ISR中标识按键按下,然后在中断源端清pend
3. S5PV210 看门狗模块解析
1)看门狗作用
A. 嵌入式系统在运行过程中难免出现死机或跑飞的情况,此时需要一种自动复位的功能(尤其是无人值守设备)。
B. 看门狗(watchdog timer)作为SoC上的一个硬件模块,作用就是在系统死机时,帮助系统实现自动复位。
C. 看门狗的特点是需要不停地接收信号(外置看门狗芯片)或重置计数值(SoC片内看门狗),以保持计数值不为0。一旦一段时间接收不到信号或计数值为0,看门狗将发出复位信号或产生中断
2)看门狗结构分析
① 看门狗本质上是一个16位计数器,当计数为0时根据配置发出中断信号/reset信号,如果使能发出reset信号则系统重启。所以为避免系统重启,需要在计数结束前重置计数值(即喂狗)。
② 时钟模块
PCLK + 2级分频
时钟源:PCLK
第1级预分频:0 ~ 2^8 - 1
第2级分频:4档固定分频系数
watchdog时钟 = PCLK / (Prescaler value + 1) / Division_factor
③ 计数模块
WTCNT:当前计数值
WTDAT:设置超时计数值
说明1:在启动watchdog时,WTDAT中的数值不会自动加载到WTCNT中,所以在启动前需要给WTCNT设置初始值。
说明2:WTCNT的默认初始值为0x8000,如果使用该默认值启动,计数结束后WTDAT中的值会被自动加载到WTCNT中。
如此设计是因为watchdog还可以作为普通计数器使用,但寄存器中并没有设置重新装载的栏位(非watchdog计数器中均有此栏位)。
注意:上述的理解是有偏差的,下文会根据实验现象进行总结~~
④ 信号模块
说明1:根据配置,可以在计数结束后发出reset信号或触发中断。
说明2:一般作为watchdog使用时发出reset信号,作为普通计数器使用时可以触发中断
3)看门狗操作流程
① 设置看门狗中断操作
包括全局中断(CPSR中的I位)、看门狗中断使能(中断源 & VIC)、看门狗中断向量的定义(VIC)
如果只是进行复位操作,则无需此步骤
② 设置WTCON寄存器
包括预分频比例因子、分频系数、中断使能、复位使能
③ 设置WTDAT & WTCNT寄存器
④ 启动看门狗定时器
将WTCON的bit[0] & bit[5]置为1,就启动了看门狗定时器
4)关闭看门狗编程实例
A. 为什么要关闭看门狗
① 根据datasheet,CPU启动后看门狗是默认工作的。
好处:看门狗立即生效没有时间空档
坏处:启动代码需要喂狗
② 为简化启动代码,才在启动代码前端关闭看门狗(越早越好,以防复位)。之后再根据需要打开(同时要提供喂狗函数)。
③ 根据之前对S5PV210启动流程的分析,iROM代码(BL0)中已经关闭了看门狗
B. 关闭看门狗实例
根据datasheet,只需将WTCON寄存器的bit[5]置零即可。但为求简便,一般直接向该寄存器写零值。
5)看门狗定时器编程实例
A. 看门狗定时器初始化代码解析
B. 看门狗定时器ISR代码解析
说明:根据芯片手册,看门狗中断源端清pend的方式是想WTCLRINT寄存器中写入任意值
C. 实验现象及分析
① 均不设置,使用默认值,约4秒打印一次
这点符合我们的时钟频率设置和0x8000的默认初始值
② 均设置10000,1秒多打印一次
③ 设置WTDAT,不设置WTCNT,第一次4秒打印,后续的1秒打印
根据上述实验现象,就可以正确理解WTDAT & WTCNT的设置,现总结如下:
① S5PV210的watchdog模块使用auto-reload的方式工作(而PWM需要配置),一次计数完成后会将WTDAT中的数值载入WTCNT中并开始新一轮计数
② 对WTDAT载入规则的正确理解(其实手册写得是有些歧义的~~)
在启动看门狗时,WTDAT中的数值不会被自动加载到WTCNT中(其实PWM中也是如此,首次计数需要设置manual-update),如果不设置WTCNT,首次计数会采用默认值0x8000,待首次计数完成后会将WTDAT中的值加载到WTCNT中,并开始下一轮计数
手册中所谓如果将WTDAT设置为0x8000并启动首次计数,WTDAT中的值会被自动加载到WTCNT中。个人感觉是有歧义的,因为WTCNT的默认值就是0x8000。
D. 喂狗函数解析
要防止看门狗time out重启,就需要在看门狗倒计时完成前喂狗。为模拟这一过程,代码中启动Timer 4定时器,定时1s,然后在Timer的ISR中喂狗
所谓喂狗,就是在看门狗倒计时完成前重置WTCNT的值(和PWM相比,WDT相当于将实际的count-down寄存器开放给程序员操作)
4. S5PV210 RTC模块解析
1)RTC结构解析
A. RTC功能
① 为系统提供可靠的时钟
② Time Tick中断
③ Alarm 闹钟中断
B. 工作时钟 & 电池供电
RTC模块使用32.768KHz的外部时钟,核心板中接线如下
RTC控制器可以由后备电池提供电力,当系统电源关闭时,CPU和RTC逻辑电路均断开,后备电池仅驱动RTC部件的振荡电路和BCD计数器
C. 实时时钟模块
① RTC模块可提供格式为年月日星期时分秒的实时时钟,并且能够对闰年的年月日进行自动处理
② 实时时钟模块使用的时钟由clock divider产生,频率为1Hz
D. Alarm闹钟模块
① Alarm闹钟模块支持根据年月日时分秒的标准单独或组合设置闹钟
② 当设定的闹钟到期时,在正常模式下会触发ALARM_INT中断;在节电模式下会同时发出ALARM_WK信号并触发ALARM_INT中断
注意:PWM定时器是根据时间段定时,而Alarm模块是在设置的时间点触发中断
E. Time Tick模块
① Time Tick模块使用的时钟由RTC内部的时钟分频器对32.768KHz的外部时钟进行分频得来,可选的时钟设置及分辨率(此处即最小计时周期)如下,
② Time Tick计时周期如下,
③ 依据上表,RTC模块可以产生毫秒级时钟中断,该中断可供RTOS内核使用
2)核心寄存器解析
A. INTP
该寄存器用于在RTC的ISR中清pend(向对应位写1)
B. RTCCON
说明1:RTCEN位控制所有CPU和RTC的数据交互,所以在对RTC的任何之前需要将该位置1,在操作完成后将该位清零。
说明2:为了RTC计时的精确,在设置RTC之前需要复位clock divider(通过CLKRST位)
C. TICCNT & CURTICCNT
TICCNT为Time Tick设置的计数值,CURTICCNT为当前计数值
D. RTCALARM
该寄存器用于使能Alarm功能,设置时首先将ALMEN位置1,然后将需要设置闹钟的位置1(e.g. 每到第30秒设置闹钟,则需要将SECEN置1)
注意:在BCD计数器中有星期寄存器,但是设置闹钟时并没有以星期为标准
E. ALMSEC & ALMMIN & ALMHOUR等
以BCD格式设置的闹钟时间
F. BCDSEC & BCDMIN & BCDHOUR等
以BCD格式设置的实时时钟时间
说明1:设置实时时钟和Alarm闹钟时都要注意年的处理方式,寄存器中只提供了3位BCD码,所及在设置时一般会以某年为标准(e.g. 2000年)
说明2:读取BCD计数器时的误差
当获取完整RTC时间时,需要读取7个寄存器,其中的耗时可能会导致误差。当读到的BCDSEC值为0时,说明已经计时满1分钟,并且已经发生进位,此时之前读到的数据可能有误(e.g. 1:59时读取,而读的过程中时间变为2:00,此时之前读到的BCDMIN为1,已经需要修正)
因此建议如果读到的BCDSEC值为0,则重新读取一遍
3)RTC时间设置 & 读取代码解析
说明1:在对RTC的操作前后,分别开启/关闭RTC控制开关
说明2:在设置时间之前,将clock divider复位
4)Tick中断代码解析
5)Alarm闹钟中断代码解析
说明:使能RTC Alarm中断时需要同时将ALMEN和SECEN置1