原文 http://blog.sina.com.cn/s/blog_874d30c90101nzz1.html
下面开始分析main函数中的第三句:
// Make sure supply voltage is high enough to run
zmain_vdd_check();
看这句的注释是指,确保提供的电压足够运行,那么这个函数应该就是检查电源电压值的。
进入函数体,就在Zmain.c文件中
//
//@fn zmain_vdd_check
//@brief Check if the Vdd is OK to run the processor.
//@return Return if Vdd is ok; otherwise, flash LED, then reset
//
static void zmain_vdd_check( void )
{
uint8 vdd_passed_count = 0;
bool toggle = 0;
// Repeat getting the sample until number of failures or successes hits MAX
// then based on the count value, determine if the device is ready or not
while ( vdd_passed_count < MAX_VDD_SAMPLES )
{
if ( HalAdcCheckVdd (ZMAIN_VDD_LIMIT) )
{
vdd_passed_count++; // Keep track # times Vdd passes in a row
MicroWait (10000); // Wait 10ms to try again
}
else
{
vdd_passed_count = 0; // Reset passed counter
MicroWait (50000); // Wait 50ms
MicroWait (50000); // Wait another 50ms to try again
}
// toggle LED1 and LED2
if (vdd_passed_count == 0)
{
if ((toggle = !(toggle)))
HAL_TOGGLE_LED1();
else
HAL_TOGGLE_LED2();
}
}
// turn off LED1
HAL_TURN_OFF_LED1();
HAL_TURN_OFF_LED2();
}
首先定义了两个变量,
uint8 vdd_passed_count = 0;
bool toggle = 0;
见名之意,第一个变量应该是一个计数器,用于计算电压值超过某一个标志的数量。
第二个变量就是一个布尔类型的开关值。
然后往下看,看到了两行注释:
// Repeat getting the sample until number of failures or successes hits MAX
// then based on the count value, determine if the device is ready or not
意思大体是指,不断的进行采样,直到失败的次数或是成功的次数超过设定好的一个MAX值,然后根据这个次数来判断设备就绪了。
那我们就看是怎么完成这个操作的吧!
大条件while ( vdd_passed_count < MAX_VDD_SAMPLES ),这与刚才的注释吻合。
不过我们要关注一下MAX_VDD_SAMPLES的值:
#define MAX_VDD_SAMPLES 3 也就是说采样的次数就3次。
if ( HalAdcCheckVdd (ZMAIN_VDD_LIMIT) )
{
vdd_passed_count++; // Keep track # times Vdd passes in a row
MicroWait (10000); // Wait 10ms to try again
}
else
{
vdd_passed_count = 0; // Reset passed counter
MicroWait (50000); // Wait 50ms
MicroWait (50000); // Wait another 50ms to try again
}
(1)看一下HalAdcCheckVdd:
//
//@fn HalAdcCheckVdd
//@brief Check the Vdd and return TRUE if it greater than or equal the limit
//@param limit - limit that needs to be checked with the Vdd
//@return TRUE if Vdd >= limit, FALSE otherwise
//
bool HalAdcCheckVdd (uint8 limit)
{
uint16 value;
//Clear ADC interrupt flag
ADCIF = 0;
//Setup the new value for conversion
ADCCON3 = (HAL_ADC_REF_125V | HAL_ADC_DEC_064 | HAL_ADC_CHN_VDD3);
//Wait for the conversion to finish
while ( !ADCIF );
//Get the result
value = ADCL;
value |= ((uint16) ADCH) << 8;
//Check the limit and return
return ( value >= HalAdcVddLimit[limit] );
}
看样子我们得先分析一下这个函数了……
先看注释,注释说这个函数是用来检查VDD的值是否大于或等于 limit值的,如果大于或等于limit就返回TRUE。
#define ZMAIN_VDD_LIMIT HAL_ADC_VDD_LIMIT_4
#define HAL_ADC_VDD_LIMIT_4 0x04
这里的limit值是4。
那又是怎么判断的呢?
先梳理里面的SFR、宏定义以及一些变量:
SFRBIT( TCON , 0x88, URX1IF, _TCON6, ADCIF, _TCON4, URX0IF, IT1, RFERRIF, IT0 )
这里用到了ADCIF:它位于TCON的第5位:
![Zigbee学习笔记(四)——zmain_vdd_check(); Zigbee学习笔记(四)——zmain_vdd_check();](https://i-blog.csdnimg.cn/blog_migrate/2ebb66eac7a54dca6bdd6bbe2ef33ace.jpeg)
也就是ADCIF是ADC中断标志位,当产生了一个ADC中断时,这一位就被置为了1,然后等待CPU的处理。并且CPU处理后,这一位就会自动清除了。
SFR( ADCCON3 , 0xB6 ) // ADC控制寄存器3
![Zigbee学习笔记(四)——zmain_vdd_check(); Zigbee学习笔记(四)——zmain_vdd_check();](https://i-blog.csdnimg.cn/blog_migrate/18ada526ad8749ace95d291f3a70e700.jpeg)
SFR( ADCL , 0xBA ) // ADC数据低位
SFR( ADCH , 0xBB ) // ADC数据高位
![Zigbee学习笔记(四)——zmain_vdd_check(); Zigbee学习笔记(四)——zmain_vdd_check();](https://i-blog.csdnimg.cn/blog_migrate/e0a67d4313c673a21ab112a9986c5d93.jpeg)
#define HAL_ADC_REF_125V 0x00 //Internal 1.25V Reference
#define HAL_ADC_DEC_064 0x00 //Decimate by 64 : 8-bit resolution
#define HAL_ADC_CHN_VDD3 0x0f //VDD/3
static __code const uint16 HalAdcVddLimit[] =
{
0x369C, // VDD Limit - 1.6v
0x3A06, // VDD Limit - 1.7v
0x3D70, // VDD Limit - 1.8v
0x40D9, // VDD Limit - 1.9v
0x4443, // VDD Limit - 2.0v
0x47AD, // VDD Limit - 2.1v
0x4B17, // VDD Limit - 2.2v
0x4E81, // VDD Limit - 2.3v
0x51EA, // VDD Limit - 2.4v
};
然后分析这个函数执行的过程,
首先将ADCIF置0:
ADCIF = 0;
这是在使用中断前要做的事情(突然想起来在main中的第一句不是关闭了所有中断么?所以大胆估计这个中断标志位就算被置位,也不会触发中断响应的函数)
接着:
ADCCON3 = (HAL_ADC_REF_125V | HAL_ADC_DEC_064 | HAL_ADC_CHN_VDD3);
看注释是说准备新的转换的值,HAL_ADC_REF_125V | HAL_ADC_DEC_064 | HAL_ADC_CHN_VDD3的结果为0x0f,查看ADCCON3被置为0x0f时的含义,得知如下设置:
额外转换的参考电压——内部参考电压:根据前面的宏定义可以看到这个参考电压为1.25V
额外转换的抽取率 ——64抽取率:抽取率是用来决定ADC转换时间的,CC2530手册中有公式:
Tconv=(抽取率+16)x0.25μs,64抽取率对应转换时间为20μs。
单个通道选择 ——VDD/3:这指的是ADC输入的引脚。
这些个概念什么意思嘛!虽然以前写过AD转换的单片机程序,但是接触这些概念还是比较头疼啊!就先把数据手册关于ADC转换的地方看一下吧,大致了解了以下几个概念:
① ADC输入:端口0引脚的信号可以用作ADC输入。
片上温度传感器的输出也可以选择作为ADC的输入,用于温度测量。
还可以输入一个对应AVDD5/3的电压作为一个ADC输入。这个输入允许诸如需要在应用中实现一个电池监测器的功能。注意在这种情况下参考电压不能取决于电源电压。
② ADC运行模式:ADCCON3寄存器控制单个转换的通道号码、参考电压和抽取率。单个转换在寄存器ADCCON3写入后将立即发生,或如果一个转换序列正在进行,该序列结束之后立即发生。该寄存器位的编码和ADCCON2是完全一样的。
③ ADC参考电压:转换结果的准确性取决于参考电压的稳定性和噪音属性。希望的电压有偏差会导致ADC增益误差,与希望电压和实际电压的比例成正比。参考电压的噪音必须低于ADC的量化噪音,以确保达到规定的SNR。
④ ADC中断:当通过写ADCCON3触发的一个单个转换完成时,ADC将产生一个中断。当完成一个序列(指ADC的一系列转换)转换时,不产生一个中断(通过DMA方式)。
⑤ ADCDMA触发:每完成一个序列转换,ADC将产生一个DMA触发。当完成一个单个转换,不产生DMA触发。
⑥ ADC转换时间:执行一个转换所需的时间取决于所选的抽取率。总的来说,转换时间由以下公式给定:
Tconv=(抽取率+16)x0.25μs。
从以上这些概念可以得知,要进行ADC模数转换,就需要有一个ADC输入(需要进行转换的来源),参考电压(决定转换结果的准确性),抽取率(决定转换的时间)。而这里是要对电源进行检测,所以输入通道选择了VDD/3,没次只对一个数据进行采样,那么在采样结束之后就会产生一个中断给ADCIF置位。
当产生了中断置位后,就识别出来,然后读出其中的数值,这里我想说一下它查询这个中断位的方式,就我前面说到的,EA被关闭了,就算中断产生了也不会执行中断响应函数了,所以这里采用了while ( !ADCIF );查询方式。
然后从ADCL和ADCH中读出转换后的值:
value = ADCL;
value |= ((uint16) ADCH) << 8;
ADCL是转换结果的低8位(其中最后两位始终为0),ADCH是高8位,所以需要左移8位。
最后将value值与HalAdcVddLimit[4]相比较,也就是与2.0V相比较的。
那为什么和这个电压值比较呢?
查看手册得知2.0V是MCU工作的最低电压,这里本身就是要检查电压是否达到是否可以工作的电压值,所以自然要和2.0V进行比较了。
![Zigbee学习笔记(四)——zmain_vdd_check(); Zigbee学习笔记(四)——zmain_vdd_check();](https://i-blog.csdnimg.cn/blog_migrate/e776b815c89630794da68ad221e29754.jpeg)
如果大于2.0V就返回1,如果小于2.0V就返回0。
回到zmain_vdd_check中:
(2)如果为1:
vdd_passed_count++; // Keep track # times Vdd passes in a row
MicroWait (10000); // Wait 10ms to try again
把超过计数值加1,然后延时10ms
顺便看一下延时函数具体是怎么定义的:
#define MicroWait(t) Onboard_wait(t)
void Onboard_wait( uint16 timeout )
{
while (timeout--)
{
asm("NOP");
asm("NOP");
asm("NOP");
}
}
(3)如果为0:
vdd_passed_count = 0; // Reset passed counter
MicroWait (50000); // Wait 50ms
MicroWait (50000); // Wait another 50ms to try again
将计数器又重新置位0。
(4)如果计数器的值被置为0了,就切换LED1灯的状态:
// toggle LED1 and LED2
if (vdd_passed_count == 0)
{
if ((toggle = !(toggle)))
HAL_TOGGLE_LED1();
else
HAL_TOGGLE_LED2();
}
虽然注释中说是切换LED1和LED2的状态,但是从代码分析可以知道,LED2的状态根本不会被切换,因为条件是toggle = !(toggle),而不是!=,toggle和toggle取非的结果肯定不相同咯,所以这个条件永真,也就执行不到else了。
看一下HAL_TOGGLE_LED1的定义
#define HAL_TOGGLE_LED1() st( if (LED1_SBIT) { LED1_SBIT = 0; } else { LED1_SBIT = 1;} )
#define LED1_SBIT P1_0
SFRBIT( P1 , 0x90, P1_7, P1_6, P1_5, P1_4, P1_3, P1_2, P1_1, P1_0 )
这里就不解释了,很容易看出来,如果本来LED的引脚是0就置1,如果原来是1就置0。
(5)然后又回到while条件检查vdd_passed_count是否达到3。是就结束,否则继续循环。
这里就发现一个问题,如果符合的电压始终达不到3次,那就会在这里做死循环了,也就是说如果电压不够,就始终不能进行工作。突然发现做电压检测是十分必要的,以前在做嵌入式开发的时候很多时间会忽略这个,上课的时候老师也一直强调——电源是嵌入式开发中十分重要的一个环节,我想从这里我要意识到了这一点,如果电源不达标,很多工作都是不可靠的。
(6)总算最后一句了:
// turn off LED1
HAL_TURN_OFF_LED1();
HAL_TURN_OFF_LED2();
这里的注释又对了,关闭的是LED1。
#defineHAL_TURN_OFF_LED1() st( LED1_SBIT = LED1_POLARITY (0); )
#defineLED1_POLARITY ACTIVE_LOW //ACTIVE_HIGH
#defineACTIVE_LOW !
这里一个!的值就是0。