之前我们讲到了串口的收发数据,我们使用到了数据结构的环形对列,就是一个追赶模型,前面一个人在放数据,后面一个人在捡数据,定义两个变量来存储这两个人所走的步数,在定义一个库存变量,也就是用来表示前面一个人放数据的量减去后面一个人捡数据的量;
我们在接收中断里面判断是否满了库存,没满就可以放,放数据的意思就是定义一个数据从接收缓冲区里面拿就可以,拿完之后这个人的步数加加,然后定义一个接收函数,对后面一个捡数据的人进行处理,也就是需要拿到第二个人拿到的真实数据长度,问什么叫真实数据长度,因为可能用户提供第二个人走的步数会大于库存数,所以就需要一个真实长度设置为库存数,不可能只有这么多库存你可以那更多;
上一期内容就回顾到这,这一期我们来讲讲数模转换,首先我们要清楚一个问题,什么是模拟信号,什么是数字信号,日常生活中会用到很多的传感器,其实传感器检测的都是模拟信号,比如温度、湿度、光照度等,这些数据有一个特点就是无限个,但数字信号只有两个,我们知道现在的电子设备用到的是二极管,只具有导通和不导通两个信号,所以想要检测到温度、湿度这些数据就需要用到数模转换,而毕竟是转换,没有办法避免失真的问题,多多少少有一点;
了解了数模转换,我们就开始这期的代码编写:
1、创建ADC模块,创建模块相信大家都会:
在驱动模块中创建就可以了,然后打开IRA进行编写;
2、初始化模块,编写初始化函数:
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*函数名:AdcInit
*参数:void
*返回:none
*描述:adc初始化函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void AdcInit(void)
{
//1.1 初始化gpio
P0SEL |= 0x01;//打开P0的外设功能
P0DIR &= ~0x01;//设置P0.1 的引脚为输入模式,因为采集信息是读取
APCFG |= 0x01;//使能adc的通道0
}
之前我们就讲到过,要初始化一个功能,首先要想到大概需要初始化那些设备,比如gpio、中断、还有本身功能,反正gpio是都需要的,不过今天我们就不使用中断了,我们就初始化gpio和adc本身就可以,在做一个功能的时候一定要读一下芯片手册,不然的话会无从下手;
这里提到了一个触发问题,因为我们使用的是单个通道,所以需要设置单个adc转换的寄存器ADCCON3,说道设置了这个寄存器的话就会触发一次采集,所以我们后面写驱动函数的时候就配置这个寄存器在进行数据处理就可以;
说道通道,cc2530一共有8个ADC通道也就是我们可以连接8个传感器,但并不是同时可以测得8个传感器数据 ,而是通过轮询的方式,效率低下,但不这样成本太高,所以我们使用单个就行,如果要是用多个就需要配置ADCCON2:
这里提到一个注意点,我们需要配置APCFG寄存器,它的作用就是覆盖gpio,我们使用的通道是P0.0:
意思就是配置这个寄存器为0x01的话就不需要配置io口了,但是我们为了保险起见还是在初始化函数里面配置了io口,我还是建议大家配一下;
2、编写获取adc数值函数
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*函数名:AdcGetValue
*参数:void
*返回:float
*描述:adc数据采集函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
float AdcGetValue(void)
{
//2.4 定义两个变量,一个用来存储位数据,一个用来存储转换数据
unsigned short result;//接收的数据有效只有12位,使用16位
float ret;
//2.1 需要产生一次触发信号进行采集
ADCCON3 = 0x30;
//2.2 等待数据的采集
while(!(ADCCON1 & 0X80));
//2.3 拿到采集到的数据并且进行转换
result = (unsigned short)(ADCH<<8) + (unsigned short)ADCL;
result = result >> 4;//因为是14位的单片机,而又只有12位有效,拿到的确实16位的数据,所以我们干脆移去四位
ret = result * 3.3 / 4096;//毕竟是当做12位来算,所以我们转换的时候就除以12位的分割数,3.3是参考电压
//2.4 返回采集并且转换的结果
return ret;
}
我们定义一个返回值为float的函数来获取采集的数据,毕竟一般采集到的数据会是一个小数,而在转换数据的时候需要一个参考电压,毕竟是数模转换,我们模拟信号无限,而数字信号只有两个,所以就设置一个参考电压进行转换,我们将参考电压根据单片机的精度进行划分,cc2530是14位,但却只有12位有效,所以我们将参考电压分成4096份,然后乘以寄存器拿到的数据,就得到数字信号了,当然是很难达到这个参考电压的;
可以看到数值非常的小,那么采集信号之后 :
读取到上面连个数据寄存器的值相加就拿到位数据了,因为是8个位,高八位移位相加之后也就只有16个位,所以我们新建一个short类型的变量存储就可以,而且处于编辑器的优化问题,我们需要对为数据进行强转一下;后面移四位的原因就是只有12位数据有效的原因:
3、那么获取到数据之后就需要通过串口显示到pc上了,我们回到main函数,导入ADC模块:
//创建一个存储单个数据的数组
static unsigned char TestBuf[100];
void main(void)
{
LEDInit();
//KeyInit();
//KeyCallBackRegister(&ShowNumber);
TimerInit();
static unsigned long ledtime = 0;
TimeSetCallBack(500,TimeLed,1);
UartInit(9600,'n');
AdcInit();
//1.3定义一个需要发送数据的长度和采集数据的存储变量
unsigned char len = 0;
float ret = 0;
//1.1 通过传感器产生的数据,发送到pc端显示,
while(1)
{
//1.2 我们利用定时器1s中采集一次显示即可
if(TimerCheck(ledtime))
{
ledtime = TimerSet(1000);
//1.5 通过adc模块拿到采集到的数据
ret = AdcGetValue();
//1.4 进行数据的格式化处理
len = sprintf(TestBuf,"The value of ADC is %f",ret);
UartSend((char *)TestBuf,len);
}
}
这里我们使用多模块,利用定时器模块的定时功能,每隔1s就进行采集一次然后发送一次,这里我们使用到了sprintf(字符数组,"格式化的数据",数据)格式化字符串函数,这个函数的作用就是将数据按照我们想要的格式进行转换,并且返回一个长度,然后接口这个长度通过串口发送就可以了;
好了,那么这期就到这。