CC2530的时钟模块
(cc2530_datasheet节选翻译如下)
******************************************************************
* 作 者:fulinux
* 转载声明:点击链接
******************************************************************
******************************************************************
* 作 者:fulinux
* 转载声明:点击链接,如有不对之处,请指正
******************************************************************
/**********************************************************************************************************************************************************
* 文 件 名:main.c
×
* 功 能:实验一 系统时钟源的选择
*
* CC2530有1个内部的系统时钟。时钟源可以是1个16MHz的RC振荡器,也可以是1个32MHz的晶体
* 振荡器。时钟控制是通过使用CLKCON特殊功能寄存器来执行的。系统时钟也提供给所有的8051
* 外设。
*
* 32MHz晶体振荡器的启动时间对于某些应用而言太长了,因此CC2530可以运行在16MHz RC振荡器
* 直到晶体振荡器稳定。16MHz RC振荡器的功耗要少于晶体振荡器,但是由于它没有晶体振荡器
* 精确,因此它不适用于射频收发器。
*
* CLKCONCMD.OSC位被用来选择系统时钟源。注意:要使用射频收发器,32MHz晶体振荡器必须被选择
* 并且稳定。
*
* 注意:改变CLKCON.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< 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;
}
}
}
}