cc2530的时钟
2014-09-07 09:59
1501 查看
晶振与时钟
cc2530有一个内部系统时钟,或者说是主时钟。系统时钟源是16Mhz的RC晶振或是32MHz晶体震荡器。利用CLKCONCMD特殊功能寄存器可以控制时钟。
此外,芯片还有一个32KHz时钟源可以是RC振荡器或是晶体振荡器,同样也可以用CLKCONCMD寄存器控制。
其中只读寄存器CLKCONSTA可以用来获得当前时钟状态。高精度的晶体振荡器或是低功耗的RC振荡器都可以成为振荡器的选择,需要注意的是:RF接收器要求使用32MHz晶体振荡器。如下图所示即为时钟结构图:
晶体振荡器
在芯片内部,有2个高频振荡器,一个是32MHz的晶体振荡器,另一个是16MHz的RC晶振。32MHz晶振的启动时间对于某些应用显得有些长,因此芯片可以先在16MHz RC晶振上运行,直到晶体振荡器稳定。16MHzd的RC振荡器比晶体振荡器消耗更少能量,但是精度而言比晶体振荡器差一些,并且不能作为RF传输的运行时钟。
此外,芯片还有2个低频振荡器分别是32KHz的晶体振荡器与32KHz的RC振荡器。其中,32KHz XOSC设计运行在32.768KHz频率上,并且为需要时间准确的系统提供了一个稳定的时钟信号。校验后,32KHz的RCOSC在32.753KHz频率上运行。
需要注意的是校验只能在32MHz XOSC使能的情况下才被允许。与32KHz XOSC 振荡器相比,32KHz的RC振荡器更适宜于较少成本与能耗。
请注意:这2个32KHz的振荡器不能同时使用。
系统时钟
系统时钟来自于所选择的系统时钟源,可以是32MHz的XOSC(晶体振荡器)或者是16MHz的RCOSC(RC振荡器).寄存器CLKCONCMD中OSC位选择系统时钟源,需要指出的是,如果要用RF传输,那么32MHz的晶体振荡器必须被选择作为主时钟并且稳定。
改变CLKCONCMD中的OSC位并不能立刻改变系统时钟,只有当CLKCONSTA寄存器中的OSC位与CLKCONCMD中OSC位相同时才能起作用。这归因于对拥有稳定的时钟的需求优先于改变时钟源。此外,CLKCONCMD中的CLKSPD位如同镜子一样反映CLKCONCMD.OSC位。
当32MHz XOSC晶振选为系统时钟并且稳定之后,16MHz RC振荡器进行校验,例如CLKCONSTA寄存器的OSC位从1变为0。
32KHz时钟选择如上所述,在器件上有2个32KHz时钟源,在复位之后默认会使能32KHz的RC振荡器,并作为32KHz的时钟源,32KHz的RC振荡器能耗小,但与32KHz的晶体振荡器相比精度上差许多。被选择的32KHz时钟源用于驱动休眠时钟,为看门狗产生滴答时钟,并可以用作定时器TIM2的闸门(strobe)用来计算休眠定时器的休眠时间。定时器控制寄存器中OSC32K位用于选择振荡器,作为32KHz的时钟源。CLKCONCMD寄存器的OSC32K位可以随时写入。振荡器在活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz
RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500
ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz晶体振荡器稳定之前不能使用。
cc2530时钟源主要是4个:对于高频时钟(系统时钟)可以是外接的32MHz晶振,也可以是内部的16MHz RC振荡器;对于低频时钟(32kHz),可以是外接32.768kHz晶振,也可以是内部的32.768kHz RC振荡器。
参考帖子可以知道,石英晶体的精度高,但是耗电大、启动缓慢,而芯片内部自带的RC振荡器虽然精度略低,但启动速度快、耗电低,十分适合在低功耗场合,并且无需外接电子器件就可以工作,降低成本!在上电默认使用的是内部的RC振荡器!
为了自由配置所需要的时钟,主要借助于CLKCONCMD.OSC选择系统主时钟,而借助于CLKCONCMD.OSC32K则用于选择芯片32K时钟源!
而低功耗模式设置时,需要借助于SLEEPCMD寄存器,在swru191e(cc2530datasheet)中并没有说明SLEEPCMD第二位功能,如下所示:
但是参考cc2430芯片的说明书可以发现,对应的SLEEP寄存器则有说明,如下所示,小编的理解是,TI收购Chipcon公司,随之将cc2430
2.4GHz Zigbee解决方案也收入,进一步优化芯片,更新电路IC设计,c2530与之前推出的2430芯片相比,应该具有许多相似之处,可能是cc2530推出不久,说明文档不够详细,当然不排除TI有意隐藏芯片细节的可能,字面的解释就是,当SLEEPCMD.OSC_PD为0时,32MHz晶振与16MHz RC振荡器都会起振振
:
对于SLEEPSTA寄存器中BIT6/BIT5说明在cc2530说明书中也并没有说明,可以参考cc2430说明书中内容,其中第6位XOSC_STB表明外部高速32M晶振是否上电并稳定起振,当稳定时该位为1;同样对于第5位HFRC_STB则表明内部16MHz高速RC振荡器是否起振,并是否稳定,当16MHz
RC振荡器稳定时该位为1,如下图所示:
而当需要设置系统时钟时,借助于CLKCONCMD寄存器就可以选择芯片主时钟与32K时钟源,当设置稳定后,可以通过CLKCONSTA寄存器中第6位与第7位来反应当前时钟的选择情况,当时钟稳定后会置位相应的位,依此可以判断当前时钟设置情况!
此外,当配置完SLEEPCMD寄存器后,可以置位OSC_PD来关闭未被选择的高速时钟(16MHz
EC振荡器或是32MHz 晶体振荡器)(可能是第三位写1,来实现此功能对于CC2530),如下所示
作为演示,小编写了一个示例代码用于配置cc2530时钟系统,如下所示:void wg_sys_clk_config(uint8 mclk,uint8 lclk){ uint16 i; SLEEPCMD &= ~OSC_PD; /* turn on 16MHz
RC and 32MHz XOSC */ /* wait for 32MHz XOSC stable */ while (!(SLEEPSTA & XOSC_STB)||!(SLEEPSTA &HFRC_STB)); asm("NOP"); /* chip bug workaround */ for (i=0; i<504; i++) asm("NOP"); /* Require 63us delay for all revs */ /* Select system
clock and the source for 32K clock */ CLKCONCMD = (mclk | lclk); /* Wait for the change to be effective */ while (CLKCONSTA != (mclk | lclk));
SLEEPCMD |= OSC_PD; /* turn off the oscillator not used*/ }
其中:#ifndef
BV#define BV(n) (1 << (n))#endif/* SLEEPCMD and SLEEPSTA bit definitions */#define OSC_PD BV(2) /* 0: Both oscillators powered up and stable * 1: oscillators not stable */#define XOSC_STB BV(6) /* XOSC: powered, stable=1
*/#define HFRC_STB BV(5) /* HFRC: powerd , stable=1 */
cc2530有一个内部系统时钟,或者说是主时钟。系统时钟源是16Mhz的RC晶振或是32MHz晶体震荡器。利用CLKCONCMD特殊功能寄存器可以控制时钟。
此外,芯片还有一个32KHz时钟源可以是RC振荡器或是晶体振荡器,同样也可以用CLKCONCMD寄存器控制。
其中只读寄存器CLKCONSTA可以用来获得当前时钟状态。
CLKCONCMD:时钟频率控制寄存器。
D7位为32KHZ时间振荡器选择,,0为32KRC震荡,1为32K晶振。默认为1。
D6位为系统时钟选择。0为32M晶振,1为16M
RC震荡。当D7位为0时D6必须为1。
D5~D3为定时器输出标记。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为 1MHZ,110为500KHZ,111为250KHZ。默认为001。需要注意的是:当D6为1时,定时器频率最高可采用频率为16MHZ。
D2~D0:系统主时钟选择:000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为1MHZ,110为500KHZ,111为250KHZ。当D6为1时,系统主时钟最高可采用频率为16MHZ。
CLKCONSTA:时间频率状态寄存器。
D7位为当前32KHZ时间振荡器频率。0为32KRC震荡,1为32K晶振。
D6位为当前系统时钟选择。0为32M晶振,1为16M
RC震荡。
D5~D3为当前定时器输出标记。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为 1MHZ,110为500KHZ,111为250KHZ。
D2~D0为当前系统主时钟。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为1MHZ,110为500KHZ,111为250KHZ。
高精度的晶体振荡器或是低功耗的RC振荡器都可以成为振荡器的选择,需要注意的是:RF接收器要求使用32MHz晶体振荡器。如下图所示即为时钟结构图:
在芯片内部,有2个高频振荡器,一个是32MHz的晶体振荡器,另一个是16MHz的RC晶振。32MHz晶振的启动时间对于某些应用显得有些长,因此芯片可以先在16MHz RC晶振上运行,直到晶体振荡器稳定。16MHzd的RC振荡器比晶体振荡器消耗更少能量,但是精度而言比晶体振荡器差一些,并且不能作为RF传输的运行时钟。
此外,芯片还有2个低频振荡器分别是32KHz的晶体振荡器与32KHz的RC振荡器。其中,32KHz XOSC设计运行在32.768KHz频率上,并且为需要时间准确的系统提供了一个稳定的时钟信号。校验后,32KHz的RCOSC在32.753KHz频率上运行。
需要注意的是校验只能在32MHz XOSC使能的情况下才被允许。与32KHz XOSC 振荡器相比,2KHz的RC振荡器更适宜于较少成本与能耗。
请注意:这2个32KHz的振荡器不能同时使用。如下所示即为系统时钟控制寄存器CLKCONCMD中部分控制位:
系统时钟来自于所选择的系统时钟源,可以是32MHz的XOSC(晶体振荡器)或者是16MHz的RCOSC(RC振荡器).寄存器CLKCONCMD中OSC位选择系统时钟源,需要指出的是,如果要用RF传输,那么32MHz的晶体振荡器必须被选择作为主时钟并且稳定。
改变CLKCONCMD中的OSC位并不能立刻改变系统时钟,只有当CLKCONSTA寄存器中的OSC位与CLKCONCMD中OSC位相同时才能起作用。这归因于对拥有稳定的时钟的需求优先于改变时钟源。此外,CLKCONCMD中的CLKSPD位如同镜子一样反映CLKCONCMD.OSC位。
当32MHz XOSC晶振选为系统时钟并且稳定之后,16MHz RC振荡器进行校验,例如CLKCONSTA寄存器的OSC位从1变为0。
32KHz时钟选择
如上所述,在器件上有2个32KHz时钟源,在复位之后默认会使能32KHz的RC振荡器,并作为32KHz的时钟源,32KHz的RC振荡器能耗小,但与32KHz的晶体振荡器相比精度上差许多。被选择的32KHz时钟源用于驱动休眠时钟,为看门狗产生滴答时钟,并可以用作定时器TIM2的闸门(strobe)用来计算休眠定时器的休眠时间。定时器控制寄存器中OSC32K位用于选择振荡器,作为32KHz的时钟源。
CLKCONCMD寄存器的OSC32K位可以随时写入。振荡器在活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500
ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz晶体振荡器稳定之前不能使用。
定时器滴答值产生器
CLKCONCMD.TICKSPD寄存器控制timer1、timer3和timer4的全局预分频。预分频的值设置范围在0.25MHz和32MHz之间。
需要注意的是如果CLKCONCMD.TICKSPD显示的频率高于系统时钟,则在CLKCONSTA.TICKSPD中的实际的预分频值表明是和系统时钟的值是一样的。
数据滞留
在PM2和PM3电源模式中,绝大多数的内部电路关闭了,然而,SRAM中任保留它的内容,内部寄存器的值也会保留。
保留数据的寄存器是CPU的寄存器、外部寄存器和RF寄存器,除非另一些位域值设置的比较特殊。切换到PM2和PM3模式的现象对于软件而已是透明的。
注意在PM3模式下睡眠定时器的值不会保存。
CC2530的时钟模块
(cc2530_datasheet节选翻译如下)
******************************************************************
* 作 者:fulinux
* 转载声明:点击链接
******************************************************************
振荡器和时钟
CC2530设备有一个内部系统时钟,或者主时钟。系统时钟源可以是从16MHz RC振荡器或一个32M晶体振荡器中的一个提供。系统时钟源是由CLKCONCMD SRF控制寄存器。
还有一个32KHz的时钟源,来源可以是从RC 振荡器或者32KHz的晶体振荡器中过来,同样是由CLKCONCMD寄存器控制。
CLKCONSTA寄存器是一个制度寄存器,用来获得当前系统时钟的状态。
时钟源可以在一个精度高的晶体振荡器和一个功耗低的RC振荡器中交替选择使用。注意一点:RF的收发操作是要以32MHz的晶体振荡器为时钟源才行。
振荡器
图中给出了时钟系统中可用的时钟源的一个全貌图。
设备中存在的两个高频振荡器:
* 32MHz晶体振荡器
* 16MHz的RC振荡器
32MHz的晶体振荡器启动时间对于某些应用来说可能太长了;因此设备可以先运行在16MHz的RC振荡器中运行直到晶体振荡器稳定后在使用32MHz晶体振荡器。16MHz的RC振
荡器功耗低但是不是很准,所以不能为RF模块提供服务,只能用32MHz的晶体振荡器。
设备中存在的两个低频振荡器:
* 32 KHz晶体振荡器
* 32 KHz RC振荡器
32KHz的XOSC被设计的工作频率频率是32.768KHz并且可以为一些要求时钟准确子系统提供一个稳定的时钟信号。32KHz的RCOSC当校准后可以运行在32.753KHz频率下。校准
只能发生在当32MHz XOSC使能的情况下,可以通过使能SLEEPCMD.OSC32K_CALDIS位来关闭校准。32KHz RC振荡器相对于32KHz XOSC晶体振荡器功耗低,应该用在可以降
低成本情况下。两个振荡器不能同时工作。
系统时钟
系统时钟是由32MHz XOSC或者16MHz RCOSC两个时钟源驱动的。CLKCONCMD.OSC位用来选择系统时钟源。注意:使用RF模块时,32MHz晶体振荡器必须被选上并且运行稳定。
注意:改变CLKCONCMD.OSC位并不能立即导致系统时钟源的改变。当CLKCONSTA.OSC = CLKCONCMD.OSC时时钟源的改变才会发挥作用。这是因为设备在实际改变时钟源之前
需要稳定的时钟。还有就是注意CLKCONCMD.CLKSPD位反应着系统时钟频率,因此是CLKACONCMD.OSC位的镜子。一旦32MHz的XOSC被选中和稳定,例如,当CLKCONSTA.OSC
位从1切换到0时。
注意:从16MHz到32MHz时钟源的改变符合CLKCONCMD.TICKSPD设置。CLKCONCMD.TICKSPD设置的缓慢一些的话,当CLKCONCMD.OSC改变的话会导致实际的时钟源起作用的
时间会很长。当CLKCONCMD.TICKSPD等于000时会获得最快的切换速度。
32KHz的振荡器
默认的或者复位后32KHz RCOSC使能并且被设置作为32KHz的时钟源。其功耗低,但是相对于32KHz晶体振荡器而言精度不高,32KHz时钟源用来驱动睡眠定时器,产生看门狗的滴答值
和作为timer 2计算睡眠定时器的一个闸门。32KHz时钟源被寄存器CLKCONCMD.OSC32K位用来作为选择振荡器。CLKCONCMD.OSC32K寄存器可以在任意时间写入,但是在16MHz RC
振荡器是活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。
注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500 ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz
晶体振荡器稳定之前不能使用。
振荡器和时钟寄存器
下面是振荡器和时钟寄存器的描述,所有寄存器的位会在进入PM2和PM3时保持不变,除非有异常情况发生。
定时器滴答值产生器
CLKCONCMD.TICKSPD寄存器控制timer1、timer3和timer4的全局预分频。预分频的值设置范围在0.25MHz和32MHz之间。
需要注意的是如果CLKCONCMD.TICKSPD显示的频率高于系统时钟,则在CLKCONSTA.TICKSPD中的实际的预分频值表明是和系统时钟的值是一样的。
数据滞留
在PM2和PM3电源模式中,绝大多数的内部电路关闭了,然而,SRAM中任保留它的内容,内部寄存器的值也会保留。
保留数据的寄存器是CPU的寄存器、外部寄存器和RF寄存器,除非另一些位域值设置的比较特殊。切换到PM2和PM3模式的现象对于软件而已是透明的。
注意在PM3模式下睡眠定时器的值不会保存。
/**********************************************************************************************************************************************************
* 文 件 名:main.c
×
* 功 能:实验一 系统时钟源的选择
*
* CC2530有1个内部的系统时钟。时钟源可以是1个16MHz的RC振荡器,也可以是1个32MHz的晶体
* 振荡器。时钟控制是通过使用CLKCON特殊功能寄存器来执行的。系统时钟也提供给所有的8051
* 外设。
*
* 32MHz晶体振荡器的启动时间对于某些应用而言太长了,因此CC2530可以运行在16MHz RC振荡器
* 直到晶体振荡器稳定。16MHz RC振荡器的功耗要少于晶体振荡器,但是由于它没有晶体振荡器
* 精确,因此它不适用于射频收发器。
*
* CLKCONCMD.OSC位被用来选择系统时钟源。注意:要使用射频收发器,32MHz晶体振荡器必须被选择
* 并且稳定。
*
* 注意:改变CLKCONCMD.OSC位并不即刻生效。这是因为在实际改变时钟源之前,被选择的时钟源要
* 首先达到稳定。还要注意:CLKCONSTA.CLKSPD位将反映系统时钟频率,因此它是CLKCON.OSC位的
* “镜子”。
*
* 当SLEEPSTA.XOSC_STB为1时,表示系统报告32MHz晶体振荡器稳定。然而,这可能并不是实际情况,
× 在选择32MHz时钟作为系统时钟源之前,应该等待一个额外的64us的安全时间,可以通过增加一
* 条空指令"NOP"来实现。如果不等待,可能会造成系统崩溃。
*
* 未被选择作为系统时钟源的振荡器,通过设置SLEEP.OSC_PD为1(默认状态)将被设置为掉电模式。
* 因此,当32MHz晶体振荡器被选择作为系统时钟源后,16MHz RC振荡器可能被关闭,反之亦然。
* 当SLEEPCMD.OSC_PD为0时,这2个振荡器都被上电并运行。
* 当32MHz晶体振荡器被选择作为系统时钟源并且16MHz RC振荡器也被上电时,根据供电电压和运
* 行温度,16MHZ RC振荡器将被不断校准以确保时钟稳定。当16MHz RC振荡器被选择作为系统时钟
* 源时,该校准不被执行。
*
* 本实验将向用户演示选择不同的振荡器作为系统时钟源。本文件中有led闪烁的子程序,用户
* 可以观察在不同系统时钟源下led的闪烁情况。
*
* 在hal.h文件中包含了和系统时钟相关的一些宏,用户使用这些宏可以简化对系统时钟的控制,
* 提高代码的可读性,本实验中就使用了其中的一些宏。
*
* 注 意:本实验可在以下目标板上进行:
*
*
*
*
*
* 版 本:V1.0
**********************************************************************************************************************************************************/
#include "hal.h"
#define ON 0x01 //LED状态
#define OFF 0x00
extern void ctrPCA9554LED(UINT8 led,UINT8 operation);
extern void PCA9554ledInit();
/**************************************************************************************************
* 函数名称:halWait
*
* 功能描述:延时
*
* 参 数:wait - 延时时间
*
* 返 回 值:无
**************************************************************************************************/
void halWait(BYTE wait){
UINT32 largeWait;
if(wait == 0)
{return;}
largeWait = ((UINT16) (wait << 7));
largeWait += 114*wait;
largeWait = (largeWait >> CLKSPD);
while(largeWait--);
return;
}
/**************************************************************************************************
* 函数名称:main
*
* 功能描述:反复选择不同的振荡器作为系统时钟源,并调用led控制程序,闪烁LED灯。
*
* 参 数:无
*
* 返 回 值:无
**************************************************************************************************/
void main(void)
{
UINT8 i;
PCA9554ledInit();
while(1)
{
SET_MAIN_CLOCK_SOURCE(CRYSTAL); // 设置系统时钟源为32MHz晶体振荡器(大约用时150us),关闭16MHz RC振荡器
for (i=0;i<10;i++)
{
ctrPCA9554LED(0,ON);
halWait(200);
ctrPCA9554LED(0,OFF);
halWait(200);
}
SET_MAIN_CLOCK_SOURCE(RC); // 选择16MHz RC振荡器,关闭32MHz晶体振荡器
PCA9554ledInit();
halWait(200);
for (i=0;i<10;i++)
{
ctrPCA9554LED(1,ON);
halWait(200);
ctrPCA9554LED(1,OFF);
halWait(200);
}
}
}
/**********************************************************************************************************
* 文 件 名:iic.C
* 功 能:实验二 GPIO控制实验
* 该实验采用CC2530的I/O口(P1.0和P1.1)模拟IIC总线的SCL和SDA,然后通过IIC总线形式控制GPIO扩展芯片
* PCA9554,最后通过扩展的IO来控制LED的亮灭。
*
* 硬件连接:将OURS的CC2530RF模块插入到普通电池板或智能电池板上。
*
* P1.0 ------ SCL
* P1.1 ------ SDA
*
* 版 本:V1.0
**************************************************************************************************************/
#include "ioCC2530.h"
#include "hal_mcu.h"
#define SCL P1_0 //IIC时钟线
#define SDA P1_1 //IIC数据线
//定义IO方向控制函数
#define IO_DIR_PORT_PIN(port, pin, dir) \
do { \
if (dir == IO_OUT) \
P##port##DIR |= (0x01<<(pin)); \
else \
P##port##DIR &= ~(0x01<<(pin)); \
}while(0)
#define OSC_32KHZ 0x00 //使用外部32K晶体振荡器
//时钟设置函数
#define HAL_BOARD_INIT() \
{ \
uint16 i; \
\
SLEEPCMD &= ~OSC_PD; /* 开启 16MHz RC 和32MHz XOSC */ \
while (!(SLEEPSTA & XOSC_STB)); /* 等待 32MHz XOSC 稳定 */ \
asm("NOP"); \
for (i=0; i<504; i++) asm("NOP"); /* 延时63us*/ \
CLKCONCMD = (CLKCONCMD_32MHZ | OSC_32KHZ); /* 设置 32MHz XOSC 和 32K 时钟 */ \
while (CLKCONSTA != (CLKCONCMD_32MHZ | OSC_32KHZ)); /* 等待时钟生效*/ \
SLEEPCMD |= OSC_PD; /* 关闭 16MHz RC */ \
}
#define IO_IN 0 //输入
#define IO_OUT 1 //输出
uint8 ack; //应答标志位
uint8 PCA9554ledstate = 0; //所有LED当前状态
/******************************************************************************
* 函数名称:QWait
*
* 功能描述:1us的延时
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void QWait()
{
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");
}
/******************************************************************************
* 函数名称:Wait
*
* 功能描述:ms的延时
*
* 参 数:ms - 延时时间
*
* 返 回 值:无
*****************************************************************************/
void Wait(unsigned int ms)
{
unsigned char g,k;
while(ms)
{
for(g=0;g<=167;g++)
{
for(k=0;k<=48;k++);
}
ms--;
}
}
/******************************************************************************
* 函数名称:Start_I2c
*
* 功能描述:启动I2C总线,即发送I2C起始条件.
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Start_I2c()
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
SDA=1; /*发送起始条件的数据信号*/
asm("NOP");
SCL=1;
QWait(); /*起始条件建立时间大于4.7us,延时*/
QWait();
QWait();
QWait();
QWait();
SDA=0; /*发送起始信号*/
QWait(); /* 起始条件锁定时间大于4μs*/
QWait();
QWait();
QWait();
QWait();
SCL=0; /*钳住I2C总线,准备发送或接收数据 */
asm("NOP");
asm("NOP");
}
/******************************************************************************
* 函数名称:Stop_I2c
*
* 功能描述:结束I2C总线,即发送I2C结束条件.
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Stop_I2c()
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
SDA=0; /*发送结束条件的数据信号*/
asm("NOP"); /*发送结束条件的时钟信号*/
SCL=1; /*结束条件建立时间大于4μs*/
QWait();
QWait();
QWait();
QWait();
QWait();
SDA=1; /*发送I2C总线结束信号*/
QWait();
QWait();
QWait();
QWait();
}
/******************************************************************************
* 函数名称:SendByte
*
* 功能描述:将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
* 此状态位进行操作.(不应答或非应答都使ack=0 假)
* 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
*
* 参 数:c - 需发送的数据
*
* 返 回 值:无
*****************************************************************************/
void SendByte(uint8 c)
{
uint8 BitCnt;
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
if((c<<BitCnt)&0x80)SDA=1; /*判断发送位*/
else SDA=0;
asm("NOP");
SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/
QWait();
QWait(); /*保证时钟高电平周期大于4μs*/
QWait();
QWait();
QWait();
SCL=0;
}
QWait();
QWait();
QWait();
SDA=1; /*8位发送完后释放数据线,准备接收应答位*/
asm("NOP");
IO_DIR_PORT_PIN(1, 1, IO_IN);
SCL=1;
QWait();
QWait();
QWait();
QWait();
if(SDA==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
SCL=0;
QWait();
QWait();
IO_DIR_PORT_PIN(1, 1, IO_OUT);
}
/******************************************************************************
* 函数名称:RcvByte
*
* 功能描述:用来接收从器件传来的数据,并判断总线错误(不发应答信号),
* 发完后请用应答函数。
*
* 参 数:无
*
* 返 回 值:retc - 从器件传来的数据
*****************************************************************************/
uint8 RcvByte()
{
uint8 retc;
uint8 BitCnt;
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
retc=0;
SDA=1; /*置数据线为输入方式*/
IO_DIR_PORT_PIN(1, 1, IO_IN);
for(BitCnt=0;BitCnt<8;BitCnt++)
{
asm("NOP");
SCL=0; /*置时钟线为低,准备接收数据位*/
QWait();
QWait(); /*时钟低电平周期大于4.7μs*/
QWait();
QWait();
QWait();
SCL=1; /*置时钟线为高使数据线上数据有效*/
QWait();
QWait();
retc=retc<<1;
if(SDA==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
QWait();
QWait();
}
SCL=0;
QWait();
QWait();
IO_DIR_PORT_PIN(1, 1, IO_OUT);
return(retc);
}
/******************************************************************************
* 函数名称:Ack_I2c
*
* 功能描述:主控器进行应答信号,(可以是应答或非应答信号)
*
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Ack_I2c(uint8 a)
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
if(a==0)SDA=0; /*在此发出应答或非应答信号 */
else SDA=1;
QWait();
//QWait();
//QWait();
SCL=1;
QWait();
QWait(); /*时钟低电平周期大于4μs*/
QWait();
QWait();
QWait();
SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/
QWait();
//QWait();
}
/******************************************************************************
* 函数名称:ISendByte
*
* 功能描述:从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla.
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* c - 需发送的数据
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*****************************************************************************/
uint8 ISendByte(uint8 sla,uint8 c)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(c); /*发送数据*/
if(ack==0)return(0);
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:ISendStr
*
* 功能描述:从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
* 地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* suba - 从器件子地址
* *s - 数据
* no - 数据字节数目
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
* 注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 ISendStr(uint8 sla,uint8 suba,uint8 *s,uint8 no)
{
uint8 i;
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
if(ack==0)return(0);
for(i=0;i<no;i++)
{
SendByte(*s); /*发送数据*/
if(ack==0)return(0);
s++;
}
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:IRcvByte
*
* 功能描述:从启动总线到发送地址,读数据,结束总线的全过程,从器件地
* 址sla,返回值在c. 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* *c - 需发送的数据
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
*注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 IRcvByte(uint8 sla,uint8 *c)
{
Start_I2c(); /*启动总线*/
SendByte(sla+1); /*发送器件地址*/
//SendByte(sla);
if(ack==0)return(0);
*c=RcvByte(); /*读取数据*/
Ack_I2c(1); /*发送非就答位*/
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:IRcvStr
*
* 功能描述:从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
* 地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* suba - 从器件子地址
* *s - 数据
* no - 数据字节数目
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
* 注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 IRcvStr(uint8 sla,uint8 suba,uint8 *s,uint8 no)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
// if(ack==0)return(0);
// SendByte(sla+1);
if(ack==0)return(0);
while(no > 0)
{
*s++ = RcvByte();
if(no > 1) Ack_I2c(0); /*发送就答位*/
else Ack_I2c(1); /*发送非应位*/
no--;
}
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:ctrPCA9554LED
*
* 功能描述:通过IIC总线控制PCA9554的输出,进而控制相应的LED。
*
*
* 参 数:LED - 所控制的LED
* operation - 开或关操作
*
* 返 回 值:无
*
*
* 注 意:PCA9554的地址为:0x40
*****************************************************************************/
void ctrPCA9554LED(uint8 led,uint8 operation)
{
uint8 output = 0x00;
uint8 *data = 0;
if(ISendStr(0x40,0x03,&output,1)) //配置PCA9554寄存器
{
switch(led)
{
case 0: //LED0控制
if (operation)
{
output = PCA9554ledstate & 0xfe;
}
else
{
output = PCA9554ledstate | 0x01;
}
break;
case 1: //LED1控制
if (operation)
{
output = PCA9554ledstate & 0xfd;
}
else
{
output = PCA9554ledstate | 0x02;
}
break;
case 2: //LED2控制
if (operation)
{
output = PCA9554ledstate & 0xf7;
}
else
{
output = PCA9554ledstate | 0x08;
}
break;
case 3: //LED3控制
if (operation)
{
output = PCA9554ledstate & 0xfb;
}
else
{
output = PCA9554ledstate | 0x04;
}
break;
case 4: //LED4控制
if (operation)
{
output = PCA9554ledstate & 0xdf;
}
else
{
output = PCA9554ledstate | 0x20;
}
break;
case 5: //LED5控制
if (operation)
{
output = PCA9554ledstate & 0xef;
}
else
{
output = PCA9554ledstate | 0x10;
}
break;
default:break;
}
if(ISendStr(0x40,0x01,&output,1)) //写PCA9554输出寄存器
{
if(IRcvByte(0x40,data)) //读PCA9554输出寄存器
{
PCA9554ledstate = *data;
}
}
}
}
/******************************************************************************
* 函数名称:PCA9554ledInit
*
* 功能描述:初始化6个LED,即关闭所有的LED
*
* 参 数:无
*
* 返 回 值:无
*
*****************************************************************************/
void PCA9554ledInit()
{
uint8 output = 0x00;
uint8 *data = 0;
if(ISendStr(0x40,0x03,&output,1)) //配置PCA9554寄存器
{
output = 0xbf;
if(ISendStr(0x40,0x01,&output,1)) //写输出寄存器
{
if(IRcvByte(0x40,data)) //读输出寄存器
{
PCA9554ledstate = *data;
}
}
}
}
cc2530有一个内部系统时钟,或者说是主时钟。系统时钟源是16Mhz的RC晶振或是32MHz晶体震荡器。利用CLKCONCMD特殊功能寄存器可以控制时钟。
此外,芯片还有一个32KHz时钟源可以是RC振荡器或是晶体振荡器,同样也可以用CLKCONCMD寄存器控制。
其中只读寄存器CLKCONSTA可以用来获得当前时钟状态。高精度的晶体振荡器或是低功耗的RC振荡器都可以成为振荡器的选择,需要注意的是:RF接收器要求使用32MHz晶体振荡器。如下图所示即为时钟结构图:
晶体振荡器
在芯片内部,有2个高频振荡器,一个是32MHz的晶体振荡器,另一个是16MHz的RC晶振。32MHz晶振的启动时间对于某些应用显得有些长,因此芯片可以先在16MHz RC晶振上运行,直到晶体振荡器稳定。16MHzd的RC振荡器比晶体振荡器消耗更少能量,但是精度而言比晶体振荡器差一些,并且不能作为RF传输的运行时钟。
此外,芯片还有2个低频振荡器分别是32KHz的晶体振荡器与32KHz的RC振荡器。其中,32KHz XOSC设计运行在32.768KHz频率上,并且为需要时间准确的系统提供了一个稳定的时钟信号。校验后,32KHz的RCOSC在32.753KHz频率上运行。
需要注意的是校验只能在32MHz XOSC使能的情况下才被允许。与32KHz XOSC 振荡器相比,32KHz的RC振荡器更适宜于较少成本与能耗。
请注意:这2个32KHz的振荡器不能同时使用。
系统时钟
系统时钟来自于所选择的系统时钟源,可以是32MHz的XOSC(晶体振荡器)或者是16MHz的RCOSC(RC振荡器).寄存器CLKCONCMD中OSC位选择系统时钟源,需要指出的是,如果要用RF传输,那么32MHz的晶体振荡器必须被选择作为主时钟并且稳定。
改变CLKCONCMD中的OSC位并不能立刻改变系统时钟,只有当CLKCONSTA寄存器中的OSC位与CLKCONCMD中OSC位相同时才能起作用。这归因于对拥有稳定的时钟的需求优先于改变时钟源。此外,CLKCONCMD中的CLKSPD位如同镜子一样反映CLKCONCMD.OSC位。
当32MHz XOSC晶振选为系统时钟并且稳定之后,16MHz RC振荡器进行校验,例如CLKCONSTA寄存器的OSC位从1变为0。
32KHz时钟选择如上所述,在器件上有2个32KHz时钟源,在复位之后默认会使能32KHz的RC振荡器,并作为32KHz的时钟源,32KHz的RC振荡器能耗小,但与32KHz的晶体振荡器相比精度上差许多。被选择的32KHz时钟源用于驱动休眠时钟,为看门狗产生滴答时钟,并可以用作定时器TIM2的闸门(strobe)用来计算休眠定时器的休眠时间。定时器控制寄存器中OSC32K位用于选择振荡器,作为32KHz的时钟源。CLKCONCMD寄存器的OSC32K位可以随时写入。振荡器在活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz
RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500
ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz晶体振荡器稳定之前不能使用。
cc2530时钟源主要是4个:对于高频时钟(系统时钟)可以是外接的32MHz晶振,也可以是内部的16MHz RC振荡器;对于低频时钟(32kHz),可以是外接32.768kHz晶振,也可以是内部的32.768kHz RC振荡器。
参考帖子可以知道,石英晶体的精度高,但是耗电大、启动缓慢,而芯片内部自带的RC振荡器虽然精度略低,但启动速度快、耗电低,十分适合在低功耗场合,并且无需外接电子器件就可以工作,降低成本!在上电默认使用的是内部的RC振荡器!
为了自由配置所需要的时钟,主要借助于CLKCONCMD.OSC选择系统主时钟,而借助于CLKCONCMD.OSC32K则用于选择芯片32K时钟源!
而低功耗模式设置时,需要借助于SLEEPCMD寄存器,在swru191e(cc2530datasheet)中并没有说明SLEEPCMD第二位功能,如下所示:
但是参考cc2430芯片的说明书可以发现,对应的SLEEP寄存器则有说明,如下所示,小编的理解是,TI收购Chipcon公司,随之将cc2430
2.4GHz Zigbee解决方案也收入,进一步优化芯片,更新电路IC设计,c2530与之前推出的2430芯片相比,应该具有许多相似之处,可能是cc2530推出不久,说明文档不够详细,当然不排除TI有意隐藏芯片细节的可能,字面的解释就是,当SLEEPCMD.OSC_PD为0时,32MHz晶振与16MHz RC振荡器都会起振振
:
对于SLEEPSTA寄存器中BIT6/BIT5说明在cc2530说明书中也并没有说明,可以参考cc2430说明书中内容,其中第6位XOSC_STB表明外部高速32M晶振是否上电并稳定起振,当稳定时该位为1;同样对于第5位HFRC_STB则表明内部16MHz高速RC振荡器是否起振,并是否稳定,当16MHz
RC振荡器稳定时该位为1,如下图所示:
而当需要设置系统时钟时,借助于CLKCONCMD寄存器就可以选择芯片主时钟与32K时钟源,当设置稳定后,可以通过CLKCONSTA寄存器中第6位与第7位来反应当前时钟的选择情况,当时钟稳定后会置位相应的位,依此可以判断当前时钟设置情况!
此外,当配置完SLEEPCMD寄存器后,可以置位OSC_PD来关闭未被选择的高速时钟(16MHz
EC振荡器或是32MHz 晶体振荡器)(可能是第三位写1,来实现此功能对于CC2530),如下所示
作为演示,小编写了一个示例代码用于配置cc2530时钟系统,如下所示:void wg_sys_clk_config(uint8 mclk,uint8 lclk){ uint16 i; SLEEPCMD &= ~OSC_PD; /* turn on 16MHz
RC and 32MHz XOSC */ /* wait for 32MHz XOSC stable */ while (!(SLEEPSTA & XOSC_STB)||!(SLEEPSTA &HFRC_STB)); asm("NOP"); /* chip bug workaround */ for (i=0; i<504; i++) asm("NOP"); /* Require 63us delay for all revs */ /* Select system
clock and the source for 32K clock */ CLKCONCMD = (mclk | lclk); /* Wait for the change to be effective */ while (CLKCONSTA != (mclk | lclk));
SLEEPCMD |= OSC_PD; /* turn off the oscillator not used*/ }
其中:#ifndef
BV#define BV(n) (1 << (n))#endif/* SLEEPCMD and SLEEPSTA bit definitions */#define OSC_PD BV(2) /* 0: Both oscillators powered up and stable * 1: oscillators not stable */#define XOSC_STB BV(6) /* XOSC: powered, stable=1
*/#define HFRC_STB BV(5) /* HFRC: powerd , stable=1 */
cc2530有一个内部系统时钟,或者说是主时钟。系统时钟源是16Mhz的RC晶振或是32MHz晶体震荡器。利用CLKCONCMD特殊功能寄存器可以控制时钟。
此外,芯片还有一个32KHz时钟源可以是RC振荡器或是晶体振荡器,同样也可以用CLKCONCMD寄存器控制。
其中只读寄存器CLKCONSTA可以用来获得当前时钟状态。
CLKCONCMD:时钟频率控制寄存器。
D7 | D6 | D5~D3 | D2~D0 |
32KHZ时间振荡器选择 | 系统时钟选择 | 定时器输出标记 | 系统主时钟选择 |
D6位为系统时钟选择。0为32M晶振,1为16M
RC震荡。当D7位为0时D6必须为1。
D5~D3为定时器输出标记。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为 1MHZ,110为500KHZ,111为250KHZ。默认为001。需要注意的是:当D6为1时,定时器频率最高可采用频率为16MHZ。
D2~D0:系统主时钟选择:000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为1MHZ,110为500KHZ,111为250KHZ。当D6为1时,系统主时钟最高可采用频率为16MHZ。
CLKCONSTA:时间频率状态寄存器。
D7 | D6 | D5~D3 | D2~D0 |
当前32KHZ时间振荡器 | 当前系统时钟 | 当前定时器输出标记 | 当前系统主时钟 |
D6位为当前系统时钟选择。0为32M晶振,1为16M
RC震荡。
D5~D3为当前定时器输出标记。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为 1MHZ,110为500KHZ,111为250KHZ。
D2~D0为当前系统主时钟。000为32MHZ,001为16MHZ,010为8MHZ,011为4MHZ,100为2MHZ,101为1MHZ,110为500KHZ,111为250KHZ。
高精度的晶体振荡器或是低功耗的RC振荡器都可以成为振荡器的选择,需要注意的是:RF接收器要求使用32MHz晶体振荡器。如下图所示即为时钟结构图:
在芯片内部,有2个高频振荡器,一个是32MHz的晶体振荡器,另一个是16MHz的RC晶振。32MHz晶振的启动时间对于某些应用显得有些长,因此芯片可以先在16MHz RC晶振上运行,直到晶体振荡器稳定。16MHzd的RC振荡器比晶体振荡器消耗更少能量,但是精度而言比晶体振荡器差一些,并且不能作为RF传输的运行时钟。
此外,芯片还有2个低频振荡器分别是32KHz的晶体振荡器与32KHz的RC振荡器。其中,32KHz XOSC设计运行在32.768KHz频率上,并且为需要时间准确的系统提供了一个稳定的时钟信号。校验后,32KHz的RCOSC在32.753KHz频率上运行。
需要注意的是校验只能在32MHz XOSC使能的情况下才被允许。与32KHz XOSC 振荡器相比,2KHz的RC振荡器更适宜于较少成本与能耗。
请注意:这2个32KHz的振荡器不能同时使用。如下所示即为系统时钟控制寄存器CLKCONCMD中部分控制位:
系统时钟来自于所选择的系统时钟源,可以是32MHz的XOSC(晶体振荡器)或者是16MHz的RCOSC(RC振荡器).寄存器CLKCONCMD中OSC位选择系统时钟源,需要指出的是,如果要用RF传输,那么32MHz的晶体振荡器必须被选择作为主时钟并且稳定。
改变CLKCONCMD中的OSC位并不能立刻改变系统时钟,只有当CLKCONSTA寄存器中的OSC位与CLKCONCMD中OSC位相同时才能起作用。这归因于对拥有稳定的时钟的需求优先于改变时钟源。此外,CLKCONCMD中的CLKSPD位如同镜子一样反映CLKCONCMD.OSC位。
当32MHz XOSC晶振选为系统时钟并且稳定之后,16MHz RC振荡器进行校验,例如CLKCONSTA寄存器的OSC位从1变为0。
32KHz时钟选择
如上所述,在器件上有2个32KHz时钟源,在复位之后默认会使能32KHz的RC振荡器,并作为32KHz的时钟源,32KHz的RC振荡器能耗小,但与32KHz的晶体振荡器相比精度上差许多。被选择的32KHz时钟源用于驱动休眠时钟,为看门狗产生滴答时钟,并可以用作定时器TIM2的闸门(strobe)用来计算休眠定时器的休眠时间。定时器控制寄存器中OSC32K位用于选择振荡器,作为32KHz的时钟源。
CLKCONCMD寄存器的OSC32K位可以随时写入。振荡器在活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500
ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz晶体振荡器稳定之前不能使用。
定时器滴答值产生器
CLKCONCMD.TICKSPD寄存器控制timer1、timer3和timer4的全局预分频。预分频的值设置范围在0.25MHz和32MHz之间。
需要注意的是如果CLKCONCMD.TICKSPD显示的频率高于系统时钟,则在CLKCONSTA.TICKSPD中的实际的预分频值表明是和系统时钟的值是一样的。
数据滞留
在PM2和PM3电源模式中,绝大多数的内部电路关闭了,然而,SRAM中任保留它的内容,内部寄存器的值也会保留。
保留数据的寄存器是CPU的寄存器、外部寄存器和RF寄存器,除非另一些位域值设置的比较特殊。切换到PM2和PM3模式的现象对于软件而已是透明的。
注意在PM3模式下睡眠定时器的值不会保存。
CC2530的时钟模块
(cc2530_datasheet节选翻译如下)
******************************************************************
* 作 者:fulinux
* 转载声明:点击链接
******************************************************************
振荡器和时钟
CC2530设备有一个内部系统时钟,或者主时钟。系统时钟源可以是从16MHz RC振荡器或一个32M晶体振荡器中的一个提供。系统时钟源是由CLKCONCMD SRF控制寄存器。
还有一个32KHz的时钟源,来源可以是从RC 振荡器或者32KHz的晶体振荡器中过来,同样是由CLKCONCMD寄存器控制。
CLKCONSTA寄存器是一个制度寄存器,用来获得当前系统时钟的状态。
时钟源可以在一个精度高的晶体振荡器和一个功耗低的RC振荡器中交替选择使用。注意一点:RF的收发操作是要以32MHz的晶体振荡器为时钟源才行。
振荡器
图中给出了时钟系统中可用的时钟源的一个全貌图。
设备中存在的两个高频振荡器:
* 32MHz晶体振荡器
* 16MHz的RC振荡器
32MHz的晶体振荡器启动时间对于某些应用来说可能太长了;因此设备可以先运行在16MHz的RC振荡器中运行直到晶体振荡器稳定后在使用32MHz晶体振荡器。16MHz的RC振
荡器功耗低但是不是很准,所以不能为RF模块提供服务,只能用32MHz的晶体振荡器。
设备中存在的两个低频振荡器:
* 32 KHz晶体振荡器
* 32 KHz RC振荡器
32KHz的XOSC被设计的工作频率频率是32.768KHz并且可以为一些要求时钟准确子系统提供一个稳定的时钟信号。32KHz的RCOSC当校准后可以运行在32.753KHz频率下。校准
只能发生在当32MHz XOSC使能的情况下,可以通过使能SLEEPCMD.OSC32K_CALDIS位来关闭校准。32KHz RC振荡器相对于32KHz XOSC晶体振荡器功耗低,应该用在可以降
低成本情况下。两个振荡器不能同时工作。
系统时钟
系统时钟是由32MHz XOSC或者16MHz RCOSC两个时钟源驱动的。CLKCONCMD.OSC位用来选择系统时钟源。注意:使用RF模块时,32MHz晶体振荡器必须被选上并且运行稳定。
注意:改变CLKCONCMD.OSC位并不能立即导致系统时钟源的改变。当CLKCONSTA.OSC = CLKCONCMD.OSC时时钟源的改变才会发挥作用。这是因为设备在实际改变时钟源之前
需要稳定的时钟。还有就是注意CLKCONCMD.CLKSPD位反应着系统时钟频率,因此是CLKACONCMD.OSC位的镜子。一旦32MHz的XOSC被选中和稳定,例如,当CLKCONSTA.OSC
位从1切换到0时。
注意:从16MHz到32MHz时钟源的改变符合CLKCONCMD.TICKSPD设置。CLKCONCMD.TICKSPD设置的缓慢一些的话,当CLKCONCMD.OSC改变的话会导致实际的时钟源起作用的
时间会很长。当CLKCONCMD.TICKSPD等于000时会获得最快的切换速度。
32KHz的振荡器
默认的或者复位后32KHz RCOSC使能并且被设置作为32KHz的时钟源。其功耗低,但是相对于32KHz晶体振荡器而言精度不高,32KHz时钟源用来驱动睡眠定时器,产生看门狗的滴答值
和作为timer 2计算睡眠定时器的一个闸门。32KHz时钟源被寄存器CLKCONCMD.OSC32K位用来作为选择振荡器。CLKCONCMD.OSC32K寄存器可以在任意时间写入,但是在16MHz RC
振荡器是活跃的系统时钟源之前是不会起作用的。当系统时钟从16MHz改变为32MHz的晶体振荡器(CLKCONCMD.OSC从1到0)一旦32KHz RC振荡器被选中了它的的校验就启动了并且被执行。在校准期间,32MHz晶体振荡器的一个分频量会被使用。32KHzRCOSC振荡器校准后的结果是它会工作在32.753kHz上。32kHz RC振荡器校准时间可能要2ms时间来完成。可以设置SLEEPCMD.OSC32K_CALDIS位设置为1的话,会关闭校准。在校准结束时,会在32KHz时钟源上产生一个额外的脉冲,会导致睡眠定时器增加1。
注意:当切换到32KHz晶体振荡器后和从32KHz晶体振荡器被设置的PM3模式唤醒时,振荡器稳定到准确频率的时间在500 ms以上。睡眠定时器、看门狗定时器和时钟损失探测器在32KHz
晶体振荡器稳定之前不能使用。
振荡器和时钟寄存器
下面是振荡器和时钟寄存器的描述,所有寄存器的位会在进入PM2和PM3时保持不变,除非有异常情况发生。
定时器滴答值产生器
CLKCONCMD.TICKSPD寄存器控制timer1、timer3和timer4的全局预分频。预分频的值设置范围在0.25MHz和32MHz之间。
需要注意的是如果CLKCONCMD.TICKSPD显示的频率高于系统时钟,则在CLKCONSTA.TICKSPD中的实际的预分频值表明是和系统时钟的值是一样的。
数据滞留
在PM2和PM3电源模式中,绝大多数的内部电路关闭了,然而,SRAM中任保留它的内容,内部寄存器的值也会保留。
保留数据的寄存器是CPU的寄存器、外部寄存器和RF寄存器,除非另一些位域值设置的比较特殊。切换到PM2和PM3模式的现象对于软件而已是透明的。
注意在PM3模式下睡眠定时器的值不会保存。
/**********************************************************************************************************************************************************
* 文 件 名:main.c
×
* 功 能:实验一 系统时钟源的选择
*
* CC2530有1个内部的系统时钟。时钟源可以是1个16MHz的RC振荡器,也可以是1个32MHz的晶体
* 振荡器。时钟控制是通过使用CLKCON特殊功能寄存器来执行的。系统时钟也提供给所有的8051
* 外设。
*
* 32MHz晶体振荡器的启动时间对于某些应用而言太长了,因此CC2530可以运行在16MHz RC振荡器
* 直到晶体振荡器稳定。16MHz RC振荡器的功耗要少于晶体振荡器,但是由于它没有晶体振荡器
* 精确,因此它不适用于射频收发器。
*
* CLKCONCMD.OSC位被用来选择系统时钟源。注意:要使用射频收发器,32MHz晶体振荡器必须被选择
* 并且稳定。
*
* 注意:改变CLKCONCMD.OSC位并不即刻生效。这是因为在实际改变时钟源之前,被选择的时钟源要
* 首先达到稳定。还要注意:CLKCONSTA.CLKSPD位将反映系统时钟频率,因此它是CLKCON.OSC位的
* “镜子”。
*
* 当SLEEPSTA.XOSC_STB为1时,表示系统报告32MHz晶体振荡器稳定。然而,这可能并不是实际情况,
× 在选择32MHz时钟作为系统时钟源之前,应该等待一个额外的64us的安全时间,可以通过增加一
* 条空指令"NOP"来实现。如果不等待,可能会造成系统崩溃。
*
* 未被选择作为系统时钟源的振荡器,通过设置SLEEP.OSC_PD为1(默认状态)将被设置为掉电模式。
* 因此,当32MHz晶体振荡器被选择作为系统时钟源后,16MHz RC振荡器可能被关闭,反之亦然。
* 当SLEEPCMD.OSC_PD为0时,这2个振荡器都被上电并运行。
* 当32MHz晶体振荡器被选择作为系统时钟源并且16MHz RC振荡器也被上电时,根据供电电压和运
* 行温度,16MHZ RC振荡器将被不断校准以确保时钟稳定。当16MHz RC振荡器被选择作为系统时钟
* 源时,该校准不被执行。
*
* 本实验将向用户演示选择不同的振荡器作为系统时钟源。本文件中有led闪烁的子程序,用户
* 可以观察在不同系统时钟源下led的闪烁情况。
*
* 在hal.h文件中包含了和系统时钟相关的一些宏,用户使用这些宏可以简化对系统时钟的控制,
* 提高代码的可读性,本实验中就使用了其中的一些宏。
*
* 注 意:本实验可在以下目标板上进行:
*
*
*
*
*
* 版 本:V1.0
**********************************************************************************************************************************************************/
#include "hal.h"
#define ON 0x01 //LED状态
#define OFF 0x00
extern void ctrPCA9554LED(UINT8 led,UINT8 operation);
extern void PCA9554ledInit();
/**************************************************************************************************
* 函数名称:halWait
*
* 功能描述:延时
*
* 参 数:wait - 延时时间
*
* 返 回 值:无
**************************************************************************************************/
void halWait(BYTE wait){
UINT32 largeWait;
if(wait == 0)
{return;}
largeWait = ((UINT16) (wait << 7));
largeWait += 114*wait;
largeWait = (largeWait >> CLKSPD);
while(largeWait--);
return;
}
/**************************************************************************************************
* 函数名称:main
*
* 功能描述:反复选择不同的振荡器作为系统时钟源,并调用led控制程序,闪烁LED灯。
*
* 参 数:无
*
* 返 回 值:无
**************************************************************************************************/
void main(void)
{
UINT8 i;
PCA9554ledInit();
while(1)
{
SET_MAIN_CLOCK_SOURCE(CRYSTAL); // 设置系统时钟源为32MHz晶体振荡器(大约用时150us),关闭16MHz RC振荡器
for (i=0;i<10;i++)
{
ctrPCA9554LED(0,ON);
halWait(200);
ctrPCA9554LED(0,OFF);
halWait(200);
}
SET_MAIN_CLOCK_SOURCE(RC); // 选择16MHz RC振荡器,关闭32MHz晶体振荡器
PCA9554ledInit();
halWait(200);
for (i=0;i<10;i++)
{
ctrPCA9554LED(1,ON);
halWait(200);
ctrPCA9554LED(1,OFF);
halWait(200);
}
}
}
/**********************************************************************************************************
* 文 件 名:iic.C
* 功 能:实验二 GPIO控制实验
* 该实验采用CC2530的I/O口(P1.0和P1.1)模拟IIC总线的SCL和SDA,然后通过IIC总线形式控制GPIO扩展芯片
* PCA9554,最后通过扩展的IO来控制LED的亮灭。
*
* 硬件连接:将OURS的CC2530RF模块插入到普通电池板或智能电池板上。
*
* P1.0 ------ SCL
* P1.1 ------ SDA
*
* 版 本:V1.0
**************************************************************************************************************/
#include "ioCC2530.h"
#include "hal_mcu.h"
#define SCL P1_0 //IIC时钟线
#define SDA P1_1 //IIC数据线
//定义IO方向控制函数
#define IO_DIR_PORT_PIN(port, pin, dir) \
do { \
if (dir == IO_OUT) \
P##port##DIR |= (0x01<<(pin)); \
else \
P##port##DIR &= ~(0x01<<(pin)); \
}while(0)
#define OSC_32KHZ 0x00 //使用外部32K晶体振荡器
//时钟设置函数
#define HAL_BOARD_INIT() \
{ \
uint16 i; \
\
SLEEPCMD &= ~OSC_PD; /* 开启 16MHz RC 和32MHz XOSC */ \
while (!(SLEEPSTA & XOSC_STB)); /* 等待 32MHz XOSC 稳定 */ \
asm("NOP"); \
for (i=0; i<504; i++) asm("NOP"); /* 延时63us*/ \
CLKCONCMD = (CLKCONCMD_32MHZ | OSC_32KHZ); /* 设置 32MHz XOSC 和 32K 时钟 */ \
while (CLKCONSTA != (CLKCONCMD_32MHZ | OSC_32KHZ)); /* 等待时钟生效*/ \
SLEEPCMD |= OSC_PD; /* 关闭 16MHz RC */ \
}
#define IO_IN 0 //输入
#define IO_OUT 1 //输出
uint8 ack; //应答标志位
uint8 PCA9554ledstate = 0; //所有LED当前状态
/******************************************************************************
* 函数名称:QWait
*
* 功能描述:1us的延时
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void QWait()
{
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");asm("NOP");
asm("NOP");
}
/******************************************************************************
* 函数名称:Wait
*
* 功能描述:ms的延时
*
* 参 数:ms - 延时时间
*
* 返 回 值:无
*****************************************************************************/
void Wait(unsigned int ms)
{
unsigned char g,k;
while(ms)
{
for(g=0;g<=167;g++)
{
for(k=0;k<=48;k++);
}
ms--;
}
}
/******************************************************************************
* 函数名称:Start_I2c
*
* 功能描述:启动I2C总线,即发送I2C起始条件.
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Start_I2c()
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
SDA=1; /*发送起始条件的数据信号*/
asm("NOP");
SCL=1;
QWait(); /*起始条件建立时间大于4.7us,延时*/
QWait();
QWait();
QWait();
QWait();
SDA=0; /*发送起始信号*/
QWait(); /* 起始条件锁定时间大于4μs*/
QWait();
QWait();
QWait();
QWait();
SCL=0; /*钳住I2C总线,准备发送或接收数据 */
asm("NOP");
asm("NOP");
}
/******************************************************************************
* 函数名称:Stop_I2c
*
* 功能描述:结束I2C总线,即发送I2C结束条件.
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Stop_I2c()
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
SDA=0; /*发送结束条件的数据信号*/
asm("NOP"); /*发送结束条件的时钟信号*/
SCL=1; /*结束条件建立时间大于4μs*/
QWait();
QWait();
QWait();
QWait();
QWait();
SDA=1; /*发送I2C总线结束信号*/
QWait();
QWait();
QWait();
QWait();
}
/******************************************************************************
* 函数名称:SendByte
*
* 功能描述:将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
* 此状态位进行操作.(不应答或非应答都使ack=0 假)
* 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
*
* 参 数:c - 需发送的数据
*
* 返 回 值:无
*****************************************************************************/
void SendByte(uint8 c)
{
uint8 BitCnt;
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
if((c<<BitCnt)&0x80)SDA=1; /*判断发送位*/
else SDA=0;
asm("NOP");
SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/
QWait();
QWait(); /*保证时钟高电平周期大于4μs*/
QWait();
QWait();
QWait();
SCL=0;
}
QWait();
QWait();
QWait();
SDA=1; /*8位发送完后释放数据线,准备接收应答位*/
asm("NOP");
IO_DIR_PORT_PIN(1, 1, IO_IN);
SCL=1;
QWait();
QWait();
QWait();
QWait();
if(SDA==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
SCL=0;
QWait();
QWait();
IO_DIR_PORT_PIN(1, 1, IO_OUT);
}
/******************************************************************************
* 函数名称:RcvByte
*
* 功能描述:用来接收从器件传来的数据,并判断总线错误(不发应答信号),
* 发完后请用应答函数。
*
* 参 数:无
*
* 返 回 值:retc - 从器件传来的数据
*****************************************************************************/
uint8 RcvByte()
{
uint8 retc;
uint8 BitCnt;
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
retc=0;
SDA=1; /*置数据线为输入方式*/
IO_DIR_PORT_PIN(1, 1, IO_IN);
for(BitCnt=0;BitCnt<8;BitCnt++)
{
asm("NOP");
SCL=0; /*置时钟线为低,准备接收数据位*/
QWait();
QWait(); /*时钟低电平周期大于4.7μs*/
QWait();
QWait();
QWait();
SCL=1; /*置时钟线为高使数据线上数据有效*/
QWait();
QWait();
retc=retc<<1;
if(SDA==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
QWait();
QWait();
}
SCL=0;
QWait();
QWait();
IO_DIR_PORT_PIN(1, 1, IO_OUT);
return(retc);
}
/******************************************************************************
* 函数名称:Ack_I2c
*
* 功能描述:主控器进行应答信号,(可以是应答或非应答信号)
*
*
* 参 数:无
*
* 返 回 值:无
*****************************************************************************/
void Ack_I2c(uint8 a)
{
IO_DIR_PORT_PIN(1, 0, IO_OUT); //设置P1.0为输出
IO_DIR_PORT_PIN(1, 1, IO_OUT); //设置P1.1为输出
if(a==0)SDA=0; /*在此发出应答或非应答信号 */
else SDA=1;
QWait();
//QWait();
//QWait();
SCL=1;
QWait();
QWait(); /*时钟低电平周期大于4μs*/
QWait();
QWait();
QWait();
SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/
QWait();
//QWait();
}
/******************************************************************************
* 函数名称:ISendByte
*
* 功能描述:从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla.
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* c - 需发送的数据
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*****************************************************************************/
uint8 ISendByte(uint8 sla,uint8 c)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(c); /*发送数据*/
if(ack==0)return(0);
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:ISendStr
*
* 功能描述:从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
* 地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* suba - 从器件子地址
* *s - 数据
* no - 数据字节数目
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
* 注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 ISendStr(uint8 sla,uint8 suba,uint8 *s,uint8 no)
{
uint8 i;
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
if(ack==0)return(0);
for(i=0;i<no;i++)
{
SendByte(*s); /*发送数据*/
if(ack==0)return(0);
s++;
}
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:IRcvByte
*
* 功能描述:从启动总线到发送地址,读数据,结束总线的全过程,从器件地
* 址sla,返回值在c. 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* *c - 需发送的数据
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
*注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 IRcvByte(uint8 sla,uint8 *c)
{
Start_I2c(); /*启动总线*/
SendByte(sla+1); /*发送器件地址*/
//SendByte(sla);
if(ack==0)return(0);
*c=RcvByte(); /*读取数据*/
Ack_I2c(1); /*发送非就答位*/
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:IRcvStr
*
* 功能描述:从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
* 地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
* 如果返回1表示操作成功,否则操作有误。
*
*
* 参 数:sla - 从器件地址
* suba - 从器件子地址
* *s - 数据
* no - 数据字节数目
*
* 返 回 值:0 -- 失败
* 1 -- 成功
*
* 注 意:使用前必须已结束总线。
*****************************************************************************/
uint8 IRcvStr(uint8 sla,uint8 suba,uint8 *s,uint8 no)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
// if(ack==0)return(0);
// SendByte(sla+1);
if(ack==0)return(0);
while(no > 0)
{
*s++ = RcvByte();
if(no > 1) Ack_I2c(0); /*发送就答位*/
else Ack_I2c(1); /*发送非应位*/
no--;
}
Stop_I2c(); /*结束总线*/
return(1);
}
/******************************************************************************
* 函数名称:ctrPCA9554LED
*
* 功能描述:通过IIC总线控制PCA9554的输出,进而控制相应的LED。
*
*
* 参 数:LED - 所控制的LED
* operation - 开或关操作
*
* 返 回 值:无
*
*
* 注 意:PCA9554的地址为:0x40
*****************************************************************************/
void ctrPCA9554LED(uint8 led,uint8 operation)
{
uint8 output = 0x00;
uint8 *data = 0;
if(ISendStr(0x40,0x03,&output,1)) //配置PCA9554寄存器
{
switch(led)
{
case 0: //LED0控制
if (operation)
{
output = PCA9554ledstate & 0xfe;
}
else
{
output = PCA9554ledstate | 0x01;
}
break;
case 1: //LED1控制
if (operation)
{
output = PCA9554ledstate & 0xfd;
}
else
{
output = PCA9554ledstate | 0x02;
}
break;
case 2: //LED2控制
if (operation)
{
output = PCA9554ledstate & 0xf7;
}
else
{
output = PCA9554ledstate | 0x08;
}
break;
case 3: //LED3控制
if (operation)
{
output = PCA9554ledstate & 0xfb;
}
else
{
output = PCA9554ledstate | 0x04;
}
break;
case 4: //LED4控制
if (operation)
{
output = PCA9554ledstate & 0xdf;
}
else
{
output = PCA9554ledstate | 0x20;
}
break;
case 5: //LED5控制
if (operation)
{
output = PCA9554ledstate & 0xef;
}
else
{
output = PCA9554ledstate | 0x10;
}
break;
default:break;
}
if(ISendStr(0x40,0x01,&output,1)) //写PCA9554输出寄存器
{
if(IRcvByte(0x40,data)) //读PCA9554输出寄存器
{
PCA9554ledstate = *data;
}
}
}
}
/******************************************************************************
* 函数名称:PCA9554ledInit
*
* 功能描述:初始化6个LED,即关闭所有的LED
*
* 参 数:无
*
* 返 回 值:无
*
*****************************************************************************/
void PCA9554ledInit()
{
uint8 output = 0x00;
uint8 *data = 0;
if(ISendStr(0x40,0x03,&output,1)) //配置PCA9554寄存器
{
output = 0xbf;
if(ISendStr(0x40,0x01,&output,1)) //写输出寄存器
{
if(IRcvByte(0x40,data)) //读输出寄存器
{
PCA9554ledstate = *data;
}
}
}
}
相关文章推荐
- cc2530-关于Contiki系统的时钟
- CC2530 输入输出配置、中断配置、时钟、串口配置
- CC2530时钟
- ZigBee_CC2530_0DH_关于协议栈时钟的一些记录
- ZigBee_CC2530_0DH_关于协议栈时钟的一些记录
- CC2530学习之时钟与振荡器
- [置顶] ZigBee研究之旅(四)---CC2530的时钟模块
- cc2530晶振与时钟
- CC2530之时钟源
- cc2530-时钟1(timer1)模模式
- cc2530晶振与时钟
- 如何动态绘制时钟
- canvas实现和时钟效果
- RFC34 关于ARC时钟的一些初步记录摘要
- zigbee网关 cc2530终端裸机串行驱动12864(字库ST7920)显示图片
- 在blog中显示天气预报、日历、时钟、MSN、QQ在线状态
- Arduino - 多线程库SCoop应用 之 时钟(计时器)
- 汇编时钟(菜菜)
- cocos2dx cygwin警告:检测到时钟错误。您的创建可能是不完整的。
- 如何在自己的博客中加入时钟、天气、流量(更新修正)1月17日