导学
本学习基于8051单片机(芯片:CC2530)
教师介绍% 记录祁瑞丽老师课堂教学 教学经验:2010-至今 研究生毕业于:燕山大学
课程介绍
前导课程:C语言程序设计、数字电路与模拟电路后继课程:嵌入式应用开发、物联网应用开发
课程主要内容
- GPIO
- 中断系统
- 定时器/计数器
- 串口通信
- ADC应用
实验内容
项目序号 | 实验项目名称 |
---|---|
1 | 外部中断应用——键控三色灯的设计 |
2 | 串行通信应用——多指令控制器设计与开发 |
3 | ADC应用——芯片温度监控系统设计 |
学习资源
第一章:认识单片机
基础结构
处理器内核(CPU)
存储器
I/O控制电路
RAM(Random Access Memory)——随机存取存储器
ROM(Read-Only Memory)——只读存储器
发展历程
不说了
特点及用途
特点:集成度高、体积小、功耗低、控制性强、易扩展、环境适应能力强
用途:智能仪表、工业控制、医疗设备
单片机内部三大资源
- Flash——程序存储空间(重复擦写1万次)早期单片机是OTPROM。
- RAM——数据存储空间。
- SFR——特殊功能寄存器。
第二章:CC2530中的GPIO
- 共21个pin
- 分为P0,P1,P2三组(8 8 5)
- 可以用作“通用I/O”和“外设I/O”
- 每个pin都有唯一的编号
- PxSEL用来设置pin是“通用/外设”
- PxDIR用来设置pin上的数据流动方向
PxSEL
在CC2530中通过PxSEL(x=0,1,2)来对GPIO的pin进行工作模式选择:
某个二进制位取值为“0”——对应的pin工作在“通用IO”模式下
某个二进制位取值为“1”——对应的pin工作在“外设IO”模式下
PxDIR
在CC2530中通过PxDIR(x=0,1,2)的数据解释为:
某个二进制位取值为“0”——对应的pin用作input
某个二进制位取值为“1”——对应的pin用作output
与或操作
实战练习
控制Led闪烁
延时函数(直接引入)
/**********************
* 文件:delay.c
************************/
/***********************
* 头文件
*************************/
#include "delay.h"
/*********************************************************
* 名称:hal_wait(u8 wait)
* 功能:硬件毫秒延时函数
* 参数:wait——延时时间(wait < 255)
* 返回:无
* 注释:CC2530系统固件库系统的精确毫秒延时函数,由TI官方提供
************************************************************/
void hal_wait(u8 wait)
{
unsigned long largeWait; //定义硬件计数临时参数
if(wait == 0) return; //如果延时参数为0,则跳出
largeWait = ((u16) (wait << 7)); //将数据扩大64倍
largeWait += 114*wait; //将延时数据扩大114倍并求和
largeWait = (largeWait >> CLKSPD); //根据系统时钟频率对延时进行放缩
while(largeWait --); //等待延时自减完成
}
/***********************************************
* 名称:delay_s()
* 功能:在延时毫秒的基础上延时秒
* 参数:times——延时时间
* 返回:无
* 注释:延时为990,用于抵消while函数的指令周期
************************************************/
void delay_s(u16 times)
{
while(times --){
delay_ms(990); //延时1S
}
}
/*********************************************
* 名称:delay_ms()
* 功能:再硬件延时上延时大于255的毫秒延时
* 参数:times——延时时间
* 返回:无
**********************************************/
void delay_ms(u16 times)
{
u16 i,j; //定于临时参数
i = times / 250; //获取要延时时长的250ms倍数部分
j = times % 250; //获取要延时时长的250ms余数部分
while(i --) hal_wait(250); //延时250毫秒
hal_wait(j); //延时剩余部分
}
/************************************
* 名称:delay_us()
* 功能:估算的微秒延时函数
* 参数:times——延时时间
* 返回:无
*************************************/
void delay_us(u16 times)
{
while (times--){
asm("NOP"); //汇编指令,空操作
asm("NOP"); //汇编指令,空操作
asm("NOP"); //汇编指令,空操作
}
}
/**************************************
* 文件:delay.h
****************************************/
/***************************************
* 头文件
*****************************************/
#include <ioCC2530.h> //引入CC2530所对应的头文件(包含各SFR的定义)
/***************************************
* 宏条件编译
***************************************/
#ifndef __DELAY_H__
#define __DELAY_H__
/*************************************
* 申明定义无符号数据类型
***************************************/
typedef unsigned char u8; //将unsigned char 声明定义为 u8
typedef unsigned short u16; //将unsigned short 声明定义为 u16
typedef unsigned long u32; //将unsigned int 声明定义为 u32
/*************************************
* 宏定义
*************************************/
#define CLKSPD (CLKCONCMD & 0x07) //宏定义系统时钟分频系数
/************************************
* 内部原型函数
*************************************/
void delay_s(u16 times); //硬件延时函数秒
void delay_ms(u16 times); //硬件延时函数毫秒
void delay_us(u16 times); //硬件延时函数微秒
void hal_wait(u8 wait); //硬件毫秒延时函数
#endif /*__DELAY_H_*/
Led控制函数
主函数
#include <ioCC2530.h>
#include "led.h"
#include "delay.h"
void main(void)
{
led_init();
while(1)
{
LED1=ON;
delay_ms(500);
LED1=OFF;
delay_ms(500);
}
}
Rgb七色灯
代码
#include "rgb.h"
#include "delay.h"
void rgb_init(void)
{
P0SEL &= ~0x0F; //配置管脚为通用IO模式
P0DIR |= 0x0F; //配置控制管脚为输入模式
RGB_R = OFF;
RGB_G = OFF;
RGB_B = OFF;
}
void rgb_blink(unsigned short rgb){
switch(rgb){
case 0x01:
RGB_R = ON;
delay_ms(500);
RGB_R = OFF;
delay_ms(500);
break;
case 0x02:
RGB_G = ON;
delay_ms(500);
RGB_G = OFF;
delay_ms(500);
break;
case 0x03:
RGB_R = ON;
delay_ms(500);
RGB_R = OFF;
delay_ms(500);
break;
case 0x04:
RGB_R = ON;
RGB_G = ON;
delay_ms(500);
RGB_R = OFF;
RGB_G = OFF;
delay_ms(500);
break;
case 0x05:
RGB_R = ON;
RGB_B = ON;
delay_ms(500);
RGB_R = OFF;
RGB_B = OFF;
delay_ms(500);
break;
case 0x06:
RGB_G = ON;
RGB_B = ON;
delay_ms(500);
RGB_G = OFF;
RGB_B = OFF;
delay_ms(500);
break;
case 0x07:
RGB_G = ON;
RGB_B = ON;
RGB_R = ON;
delay_ms(500);
RGB_G = OFF;
RGB_B = OFF;
RGB_R = OFF;
delay_ms(500);
break;
default:
break;
}
}
---------------------------------------------------------------------
#ifndef __RGB_H__
#define __RGB_H__
#define RGB_R P0_0 //RGBµÆºìÉ«¿ØÖÆÒý½Å
#define RGB_G P0_1 //RGBµÆÂÌÉ«¿ØÖÆÒý½Å
#define RGB_B P0_2 //RGBµÆÀ¶É«¿ØÖÆÒý½Å
#define BEEP P0_3 //·äÃùÆ÷¿ØÖÆÒý½Å
#define ON 0
#define OFF 1
void rgb_init(void);
void rgb_blink(unsigned short rgb);
#endif /*__RGB_H__*/
----------------------------------------------------------------------
#include <ioCC2530.h>
#include "delay.h"
#include "rgb.h"
void main(void)
{
rgb_init();
while(1)
{
rgb_blink(0x01);
rgb_blink(0x02);
rgb_blink(0x03);
rgb_blink(0x04);
rgb_blink(0x05);
rgb_blink(0x06);
rgb_blink(0x07);
//BEEP = 0;
delay_ms(1000);
//BEEP = 1;
}
}
第三章:中断
中断系统
- 中断源
- 中断服务程序
- 中断向量表
- 中断嵌套和中断判优机制
- 中断允许与屏蔽
中断源
也叫中断发生源,是指向CPU提交中断请求的设备,例如按键就叫做中断源。
一个中断控制器可以连接多个中断源,就CC2530的中断控制器来说,它可以连接18个不同的中断源。
中断服务程序
也叫中断服务函数。
当一个中断源向CPU提交了中断请求后,它必定是希望CPU放下手头的工作,去执行它所希望的功能。
我们做为程序开发人员,可以对任何外部中断源编写属于它们各自的程序功能,这些功能就叫做中断服务函数。
对于CPU来说,这些中断服务函数与普通的函数一样都是main函数以外的函数。只不过中断服务函数在定义时需要另外指定存放地址。
中断向量表
是一段连续的存储器空间,这段空间里存放的是所有中断服务函数的地址,也叫中断向量。
中断服务函数在定义以前需要明确指定存放地址,这是与普通函数定义不同的地方。
在CPU执行指令过程中调用普通函数时,只需要把函数名写在程序代码中进行调用就行了,什么时候调用就什么时候去写函数名。
但是中断服务函数是针对外部设备的请求提出来才执行的,这些中断服务函数只需要定义,不需要在main函数中调用它,当外部设备有中断请求提出后,CPU自己能判断出来是谁提出的中断,然后就按中断向量表中约定的地址,去存储器里访问中断服务函数。
这样一来,只要是外部设备提交了中断请求,不管提交多少次,什么时候提交,CPU都能正确的找到这个中断服务函数的地址去执行。
中断嵌套和中断判优机制
当CPU在处理一个外部中断的中断服务函数时,也同样是每执行完一条指令就去扫描是否还有中断请求发生。
如果有的话,就要判断一下当前正在执行的中断服务函数与刚刚提交中断请求的所要执行的功能哪个优先级高。
如果请求的高,那么停止当前的中断服务,跳到新的中断请求中去执行服务函数的功能,这就实现了中断嵌套。
当然能够实现中断嵌套的前提是有中断判优机制。
不同的单片机系统中断判优机制不同。有的单片机系统还允许程序员对中断判优进行编程,自行决定外部设备的优先级。
中断允许与屏蔽
同样CPU有时候执行的某些操作非常重要,这些操作必须连续执行不允许被外界干扰,这时就可以使用中断屏蔽机制把所有外部提交的中断请求屏蔽掉。
当CPU执行操作完成,就可以打开中断,允许接收外部的中断请求了。
总结
- CC2530 共有18个中断源
- 每个中断源都有一个中断名,这个中断名就是中断向量
- 每个中断源都有一个中断标志位
- 每个中断源都有一个“允许/屏蔽”控制位(0屏蔽1打开)
IEN0、IEN1和IE
- I--- Interrupt
- EN--- Enable
- IEN0、IEN1 和 IEN2 这三个 SFR 每个都用了 6 个 bit 来分别管理 6 个中断源的“允许/屏蔽”功能。
- 这些 bit 都有自己专属的名字,“P0IE”就是P0端口的中断请求“允许/屏蔽”位名。
- IEN0中有一个bit叫EA,用来控制系统所有的中断使能。
中断标志位的作用
当某个外部中断请求被允许,则对应的中断标志位被置位*(这个操作由硬件自动完成)*,表示希望CPU能够对其进行响应。
CPU得到中断请求后,在决定响应这个中断请求的功能时需要把中断标志位复位*(这个操作需要程序员在代码中编写完成)*,表示已经收到请求,正在对其进行服务。
只“置位”不“复位”会造成逻辑出错。
CC2530中断配置步骤
1.清除中断标志位
2.开启中断允许
3.打开全局中断允许
参数资料
参数
案例
键控LED1,LED2
电路图如下:
按键代码
#ifndef __KEY_H__
#define __KEY_H__
#include <ioCC2530.h>
#define K1 P1_2 //宏定义按键检测引脚P1_2
#define K2 P1_3 //宏定义按键检测引脚P1_3
#define UP 1 //按键弹起
#define DOWN 0 //按键被按下
void key_init(void); //管脚初始化函数
#endif /*__KEY_H_*/
#include "led.h"
void led_init(void)
{
P0SEL &= ~0x30; //p0_4, p0_5为通用IO模式
P0DIR |= 0x30; //p0_4, p0_5为输出模式
LED1 = OFF; //初始状态为关闭
LED2 = OFF; //初始状态为关闭
}
LED代码
#ifndef __LED_H__
#define __LED_H__
# include <ioCC2530.h>
#define LED1 P0_4 //P0_4控制Sensor-B LED1
#define LED2 P0_5 //P0_5控制Sensor-B LED2
#define ON 0
#define OFF 1
void led_init(void); //LED控制引脚初始化函数
#endif /*__LED_H_*/
#include "led.h"
void led_init(void)
{
P0SEL &= ~0x30; //p0_4, p0_5为通用IO模式
P0DIR |= 0x30; //p0_4, p0_5为输出模式
LED1 = OFF; //初始状态为关闭
LED2 = OFF; //初始状态为关闭
}
主函数
#include <ioCC2530.h>
#include "delay.h"
#include "led.h"
#include "key.h"
void main(){
led_init();
key_init();
while(1){
}
}
#pragma vector = P1INT_VECTOR
__interrupt void P1_ISR(void)
{
EA = 0; //关中断
if((P1IFG & 0x04 ) >0 ){ //按键中断 0000 0100 (P1_2) K1
P1IFG &= ~0x04; //中断标志清0
delay_ms(10); //按键防抖
if(K1 == DOWN){ //判断按键按下
LED1 = ON; //翻转LED0
LED2 = OFF;
}
}else if((P1IFG & 0x08 ) >0 ){ //按键中断 0000 1000(P1_3)K2
P1IFG &= ~0x08; //中断标志清0
delay_ms(10); //按键防抖
if(K2 == DOWN){ //判断按键按下
LED2 = ON; //翻转LED0
LED1 = OFF;
}
}
EA = 1; //开中断
}
优化
键控rgb三色灯
步进电机
/******************************************************
* 文件:stepmotor.c
******************************************************/
/******************************************************
* 头文件
******************************************************/
#include "stepmotor.h"
#include "delay.h"
/****************************************************
* 全局变量
****************************************************/
static unsigned int dir = 0;
/****************************************************
* 名称:stepmotor_init
* 功能:步进电机初始化
* 参数:
* 返回:无
****************************************************/
void stepmotor_init(void)
{
P0SEL &= ~0X07; //配置p0_0、p0_1、p0_2为通用IO
P0DIR |= 0X07; //配置p0_0、p0_1、p0_2为输出
}
/***************************************************
* 名称:step(int dir,int steps)
* 功能:电机单步
* 参数:int dir,int steps
* 返回:无
****************************************************/
void step(int dir,int steps)
{
int i;
if(dir) PIN_DIR = 1; //步进电机方向设置
else PIN_DIR = 0;
delay_us(5); //延时5us
for (i=0; i<steps; i++){ //步进电机旋转
PIN_STEP = 0;
delay_us(80);
PIN_STEP = 1;
delay_us(80);
}
}
/***************************************************
* 名称:forward()
* 功能:电机正转
* 参数:无
* 返回:无
***************************************************/
void forward(int data)
{
dir = 0; //步进电机方向设置
PIN_EN = 0;
step(dir, data); //启动步进电机
PIN_EN = 1;
}
/**************************************************
* 名称:reversion()
* 功能:电机反转
* 参数:无
* 返回:无
****************************************************/
void reversion(int data)
{
dir = 1; //步进电机方向设置
PIN_EN = 0;
step(dir, data); //启动步进电机
PIN_EN = 1;
}
/******************************************
* 文件:stepmotor.h
*******************************************/
#ifndef STEPMOTOR_H
#define STEPMOTOR_H
/******************************************
* 头文件
*******************************************/
#include <ioCC2530.h>
/******************************************
* 宏定义
******************************************/
#define PIN_STEP P0_0
#define PIN_DIR P0_1
#define PIN_EN P0_2
/******************************************
* 函数原型
******************************************/
void stepmotor_init(void);
void reversion(int step);
void forward(int step);
void step(int dir,int steps);
#endif //STEPMOTOR_H
第四章:定时器/计数器
振荡器
分类
晶振(Crystal Oscillator,简写为OSC):
利用“石英晶体”制作的振荡器
RC振荡器:
利用电阻电容的特性制作的振荡器
振荡频率
–振荡器可以产生一定Hz的频率。振荡的频率 f 和振荡的周期 T 之间的关系是:
T = 1 / f
1s = 1/1Hz
–有一种晶振可以产生32.768kHz的频率
32.768kHz = 32.768 × 1000 Hz = 32768 Hz = 215 Hz
振荡器的作用
–振荡器所输出的振荡频率主要用于为单片机提供系统时钟。
–单片机的CPU在系统时钟脉冲信号的驱使下“有节奏”的执行机器指令,系统时钟频率越高,单位时间内CPU可以处理的机器指令的条数也就越多,机器的运算速度就越快。
分频
为什么要分频
–晶振的自然频率远远大于1Hz,需要将晶振频率降低实现。
–单片机中CPU是“快节奏”部件,除此以外还有很多“慢节奏”部件需要相对较低的频率来控制它们的工作。
概念
如何实现分频
定时器/计数器是一种硬件装置,往往被内置在单片机内部
定时器/计数器兼有“定时”和“计数”两个功能。
通过设置定时器/计数器的“计数值”,可以实现对输入的频率进行分频的效果。
CC2530中的振荡器和时钟
■CC2530设备有一个内部系统时钟,或者主时钟。系统时钟源可以是从16MHz RC振荡器或一个32MHz晶体振荡器中的一个提供。
■还有一个32KHz的时钟源,来源可以是从RC 振荡器或者32KHz的晶体振荡器中过来。
■时钟源可以在一个精度高的晶体振荡器和一个功耗低的RC振荡器中交替选择使用。注意一点:RF的收发操作是要以32MHz的晶体振荡器为时钟源才行。
■高频振荡器:32MHz晶体振荡器和16MHz的RC振荡器
–晶体产生振荡是利用“压电效应”,32MHz的晶体振荡器从产生振荡到输出频率达到平稳状态的启动时间对于某些应用来说可能太长了;
–因此设备可以先运行在16MHz的RC振荡器中,直到晶体振荡器稳定后再使用32MHz晶体振荡器。
–16MHz的RC振荡器功耗低但是不是很准,所以不能为RF模块提供服务,只能用32MHz**的晶体振荡器。
■低频振荡器:32 kHz晶体振荡器和32 kHz RC振荡器
–32kHz的XOSC被设计的工作频率频率是32.768KHz**并且可以为一些要求时钟准确子系统提供一个稳定的时钟信号。
–低频振荡用作系统休眠时提供休眠时钟以降低功耗。
■CC2530有一个内部时钟,也叫主时钟。
–主时钟频率一般在32MHz。
–系统上电后默认使用的是16MHz的RC振荡器提供主时钟,当32MHz的晶振稳定后,可以选择由32MHz的晶振来提供主时钟。
–开发人员可以根据实际应用需要来选择不同的时钟源提供主时钟。
CC2530中的Timers
CC2530中一共有五个Timer,分别是:
■Timer1(T1)---16位定时/计数器
■Timer2(T2)--- MAC定时器
■Timer3(T3)和Timer4(T4) ---8位定时/计数器
■Sleep Timer --- 睡眠定时器
介绍
■Timer1
–Timer1是一个16位定时器,具有定时、计数以及PWM(Pulse Width Modulation,脉冲宽度调制,简称脉宽调制)功能。
–它内置可编程分频器,可以对输入的时钟信号进行分频。
–16位计数器可以实现从0x0000到0xFFFF之间任意区间的计数。
–同时Timer1拥有5个独立的均可编程的计数/捕获通道,每个通道都能实现16位的计数或者输出PWM等功能。
Timer1还可以被设置为中断请求模式,在计数结束时向CPU提出中断请求。
■Timer2
–Timer2也叫MAC Timer,是专门用来为支持IEEE 802.15.4协议(低速无线个人网络)的MAC层提供时钟信号。
–如果利用CC2530内置的RF(Radio Frequency,射频)功能进行数据传输,也就是Zigbee通信,那就需要Timer2产生的时钟信号进行数据的收发控制。
■Timer3 和 Timer4
–Timer3和Timer4都是8位的定时器,它们同样具有定时、计数以及PWM功能。
–它们跟Timer1一样也内置了可编程分频器,但他们内部的计数器是8位的,计数范围是0x00到0xFF。
–Timer3和Timer4都只有一个计数通道,这个通道同样可以产生PWM。
■Sleep Timer
–Sleep Timer是一个超低功耗定时器,可以对32kHz晶振或者32kHz RC振荡器周期进行计数。
–CC2530处于休眠状态时它可以用来作为实时时钟。
–还可以作为CC2530从休眠到唤醒时的唤醒时钟。
CC2530中的Timer1
■包含一个16位计数器,在每个活动时钟边沿递增或递减
■系统时钟可以预先被1,8,32或128分频后再作为计数时钟
■有自由运行、模计数和正计数/倒计数三种工作模式
■有5个独立的捕获/比较通道
■可提交中断请求
DMA触发功能
有5个独立的捕获/比较通道
–每个通道都可以单独设置计数的最大值,对应的SFR是T1CCn,即:
T1CC0、T1CC1、T1CC2、T1CC3、T1CC4
–捕获是指当计数器T1CNTH:T1CNTL开始计数时,可以读取它们内部的值。
■在读取时要先读取16bit的低8位,也就是T1CNTL的值,然后再读高8位,也就是T1CNTH的值。
■可以通过读取数值得到对应的时间,从而实现计时的功能。
–比较是指当计数器T1CNT开始计数时,每计一次,T1CCn都要把自己的值跟T1CNT的值进行比较。
■如果相同,则会影响当前通道的输出pin的状态(反转、复位、置位)。
■通过输出pin状态的变化,可以向CPU提出中断请求,从而实现定时的功能。
Timer1的初始化配置
■修改T1CTL的值来设置Timer1的计数频率、工作模式等
■如果工作在模计数模式或者正计数倒计数模式下时,需要配置对应通道的T1CCn的值,即先写T1CCnL,再写T1CCnH的值
■设置对应通道是比较还是捕获模式,即修改T1CCTLn的值
■如果需要使用中断还要配置相应的中断使能和中断标志位
实战与挑战
请试着用Timer1的通道0的定时功能让三色灯实现每个LED每秒闪烁一次的效果。
第五章 串行通信
通信介绍
通信系统三要素
信源、信道和信宿(发送端、传输介质、接收端)
并行通信与串行通信
串行通信介绍
串行通信中数据收发过程
同步串行通信介绍
(1)发送方在发送数据时,接收方必须也在时刻准备着接收数据。
(2)发送方和接收方的时钟频率相同,否则会造成数据错误。
(3)发送方所发送的数据帧中字符是一个挨着一个传递,字符之间没有间隙(空闲)。
异步串行通信介绍
常用异步串行通信接口--- UART
波特率
概念:指每秒钟发生的信号或符号的变化的数量,这里的符号可以是“电压、频率或相位”。
作用:用来在通信双方约定好“数据”的传输速率,这样发送端与接收端才能保持“步调一致”地进行“发和收”,否则就会出现“发得快收得慢”或者“发得慢收得快”的情况。
CC2530中的串行通信接口
CC2530中有两个独立的USART接口,分别是USART0和USART1。
它们可以分别工作在“异步串行通信”的UART模式下或者“同步串行通信”的SPI模式下。
UART操作由USART控制和状态寄存器UxCSR以及UART控制寄存器UxUCR来控制。这里的x是USART的编号,其数值为 0 或者 1。当UxCSR.MODE设置为1时,就选择了UART模式.
UxDBUF是双向缓冲寄存器,当执行数据发送时,首先将要发送的字符存入到UxDBUF中;当执行数据接收时,从UxDBUF中读取数据。
通过UxUCR来设置当前异步串行通信的配置,包括:起始位用高电平还是低电平、数据位位数、停止位位数、是否有奇偶校验位等。
通过UxBAUD和UxGCR的组合来配置USARTx的波特率。
CC2530中的两个USART都可以向MCU提出“数据接收完成中断请求”和“数据发送完成中断请求”。
串口初始化
初始化中包含对CC2530中USARTn的选择、同步异步选择、异步串行通信中数据帧格式的设置、波特率的设置等。
这里会涉及到UxCSR、UxUCR 、UxBAUD和UxGCR设置。
另外CC2530的USARTn控制器是连接在GPIO中的,还需要对相应的pin进行配置。
CC2530中外设引脚映射示例
void uart1_init(void){
//1、设置P1.6和P1.7为外设IO,对应USART1的TX和RX管脚
P1SEL |= 0xC0;
//2、选择USART1为UART模式的可选位置一
PERCFG |= 0x02;
//3、P1优先作为USART1
P2SEL &= ~0x20;
//4、设置USART1工作在UART模式下,并打开串口
U1CSR = 0xC0;
//5、设置波特率为38400bps
U1GCR = 10;
U1BAUD = 59;
//6、1位停止位,无奇偶校验位
U1UCR |= 0x00;
}
相关函数
*****************************************************************
* 名称:uart1_send_char()
* 功能:串口发送字节函数
* 参数:无
* 返回:无
*****************************************************************/
void uart1_send_char(char ch)
{
U1DBUF = ch; //将要发送的数据填入发送缓存寄存器
while (UTX1IF == 0); //等待数据发送完成
UTX1IF = 0; //发送完成后将数据清零
}
/****************************************************************
* 名称:uart1_send_string(char *Data)
* 功能:串口发送字符串函数
* 参数:无
* 返回:无
*****************************************************************/
void uart1_send_string(char *Data)
{
while (*Data != '\0') //如果检测到空字符则跳出
{
uart1_send_char(*Data++); //循环发送数据
}
}
/***************************************************************
* 名称:int uart1_recv_char()
* 功能:串口接收字节函数
* 参数:无
* 返回:无
****************************************************************/
int uart1_recv_char(void)
{
int ch; //等待数据接收完成
while (URX1IF == 0); //提取接受数据
ch = U1DBUF;
URX1IF = 0; //发送标志位清零
return ch; //返回获取到的串口数据
}
第六章 ADC模数转换
模拟信号和数字信号
术语解释
模拟量:任何两个数字之间都有无限个中间值,所以称之为连续变化的量,也就是模拟量。
数字量:数字间有一定的间隔,不是连续的,即离散的量称之为数字量。
A/D:Analog to Digital Converter,用模数转换器实现模拟量到数字量的转换,简称ADC。
D/A:Digital to Analog Converter,用数模转换器实现数字量到模拟量的转换,简称DAC。
ADC
■ADC(analog to digital converter):模数转换器,它是将连续变化的模拟信号转换为离散的数字信号的器件。
转换过程:采样 → 量化 → 编码
A/D转换示例
参数
ADC的位数:1个n位的ADC表示这个ADC共有2的n次方个刻度。
一个n 位的 ADC 表示这个ADC 共有2 的n 次方个刻度。 8位的 ADC,输出的是从0~255 一共 256 个数字量,也就是 2 的 8 次方个数据刻度
参考电压:也叫做基准电压,是ADC的参考基准,是指转换后满量程所对应的电压值,是 ADC 的一个重要指标。
要想把输入ADC的信号测量准确,那么基准源首先要准,基准源的偏差会直接导致转换结果的偏差。
分辨率:数字量变化一个最小刻度,模拟信号的变化量,定义为满刻度量程与2的n次的比值。
分辨率是数字量变化一个最小刻度时,模拟信号的变化量,定义为满刻度量程与 2n 的比值。假定 5.10V 的电压系统,使用 8 位的 ADC 进行测量,那么相当于 0~255 一共 256 个刻度把 5.10V 平均分成了 255 份,那么分辨率就是 5.10/255 = 0.02V。
转换速率:是指ADC每秒能进行采样转换的最大次数,单位是sps(samples per second)
逐次逼近型ADC的原理
CC2530中的ADC
- 可选7~12位的分辨率
- 8个独立的输入通道,可接收单端或差分信号
- 转换结束时可选中断触发或DMA触发
- 配有温度传感器输入
- 具有电池测量功能
与ADC有关的SFR
SFR | 功能 |
---|---|
ADCCON1 | 用于ADC通用控制,包括转换结束标志、ADC触发方式等 |
ADCCON2 | 用于连续ADC转换的配置 |
ADCCON3 | 用于单次ADC转换的配置 |
ADCH[7:0] | ADC转换结果的高位,即ADC[13:6] |
ADCL[7:2] | ADC转换结果的低位,即ADC[5:0] |
ADCCFG | 选择P0.0~ P0.7作为ADC输入的AIN0~AIN7 |
CC2530中的ADC编程应用
void adc_init(void)
{
//选择内部电压,12位分辨率,温度传感器输入
ADCCON3 = 0x3E;
//设置ADC的启动模式为手动
ADCCON1 |= 0x30;
// 启动ADC转换
ADCCON1 |= 0x40;
}
void adc_single_init(void)
{
//选择内部电压,12位分辨率,温度传感器输入 0011 1110
ADCCON3 = 0x3E;
//设置ADC的启动模式为手动
ADCCON1 |= 0x30; 置为1
// 启动ADC转换
ADCCON1 |= 0x40;
}
float getTemperature(void)
{
unsigned int value;
adc_single_init();
while( !(ADCCON1 & 0X80) ); //等待AD转换完成
value = ADCL >> 2;
value |=( ( (unsigned int) ADCH )<<6 ); //连接ADCH和ADCL,并赋值给value
return (value-1367.5)/4.5-4; //电压转换成温度
}