1 8051CPU
8051是一种8位元的单芯片微控制器,属于MCS-51单芯片的一种,由英特尔(Intel)公司于1981年制造。Intel公司将MCS51的核心技术授权给了很多其它公司,所以有很多公司在做以8051为核心的单片机,如Atmel、飞利浦、深联华等公司,相继开发了功能更多、更强大的兼容产品。
总体架构
CPU主要由以下几个部分组成:
(1)运算器: 运算器由算术/逻辑运算单元ALU、累加器ACC、寄存器B、暂存寄存器、程序状态字寄存器PSW组成。
(2)控制器: 控制器由指令寄存器IR、指令译码及控制逻辑电路组成。
(3)其他寄存器: 程序计数器PC、数据指针DPTR、堆栈指针SP、工作寄存器R0~R7
内存映射
SRAM映射到地址范围为0x0000到(SRAMSIZE-1)
XREG区域映射到1KB地址范围(0x6000-0x63FF)中。这些寄存器是附加寄存器,有效地扩展了SFR寄存器空间。一些外围寄存器和大多数无线电控制和数据寄存器都映射在这里。
SFR寄存器映射到地址范围(0x7080-0x70FF)闪存信息页(2KB)陕射到地址范围(0x7800-0x7FFF)中。这是一个只读区域,包含有关设备的各种信息。
SFR寄存器地址总览
2 基础实验
2.1 流水灯
实验目的
相关寄存器
下面我们以 P1.0 控制的 LED1 为例,操作 P1.0 时我们需要掌握相关寄存器的作用和配置方法。
下面我们以 P1.0 控制的 LED1 为例,操作 P1.0 时我们需要掌握相关寄存 器的作用和配置方法。
/****************************************************************************
* 文 件 名: main.c
* 作 者:
* 网 站:
* 修 订:
* 版 本: 1.0
* 描 述: 操作IO口控制4盏LED灯的全亮和全灭、闪烁、流水灯
****************************************************************************/
#include <ioCC2541.h>
#define uchar unsigned char
#define uint unsigned int
#define LED1 P1_0 //定义P1.0口为LED1控制端
#define LED2 P1_1 //定义P1.1口为LED2控制端
#define LED3 P0_4 //定义P0.4口为LED3控制端
/****************************************************************************
* 名 称: Delay_ms()
* 功 能: 以毫秒为单位延时,系统时钟不配置时默认为16M(用示波器测量相当精确)
* 入口参数: msec 延时参数,值越大,延时越久
* 出口参数: 无
****************************************************************************/
void DelayMs(uint len)
{
while(len--)
for (int i=0; i<535; i++);
}
/****************************************************************************
* 名 称: LedOnOrOff()
* 功 能: 点亮或熄灭所有LED灯
* 入口参数: mode为1时LED灯亮 mode为0时LED灯灭, 共阴极
* 出口参数: 无
****************************************************************************/
void LedOnOff(uchar mode)
{
LED1 = mode;
LED2 = mode;
LED3 = mode;
}
/****************************************************************************
* 名 称: InitLed()
* 功 能: 设置LED灯相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitLed(void)
{
P1DIR |= 0x03; //P1.0、P1.1定义为输出
P0DIR |= 0x10; //P0.4定义为输出
LedOnOff(0); //使所有LED灯默认为熄灭状态
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
uchar i;
InitLed(); //设置LED灯相关IO口
while(1) //死循环
{
LED1 = !LED1; //流水灯,初始化时LED为熄灭执行后则点亮
DelayMs(200);
LED2 = !LED2;
DelayMs(200);
LED3 = !LED3;
DelayMs(200);
for (i=0; i<2; i++) //所有灯闪烁2次
{
LedOnOff(0); //关闭所有LED灯
DelayMs(200);
LedOnOff(1); //打开所有LED灯
DelayMs(200);
}
LedOnOff(0); //使所有LED灯熄灭状态
DelayMs(200);
LedOnOff(1);
DelayMs(500);
LedOnOff(0); //使所有LED灯熄灭状态
DelayMs(200);
}
}
2.2 按键控制 LED 跑马灯
/****************************************************************************
* 文 件 名: main.c
* 作 者:
* 网 站:
* 修 订:
* 版 本: 1.0
* 描 述: 按下按键S1控制LED1.LED2.LED3实现跑马灯效果
****************************************************************************/
#include <ioCC2540.h>
#define uchar unsigned char
#define uint unsigned int
#define LED1 P1_0 // 定义P1.0口为LED1控制端
#define LED2 P1_1 // 定义P1.1口为LED2控制端
#define LED3 P0_4 // 定义P0.4口为LED3控制端
#define KEY1 P0_1 // 定义P0.1口为S1控制端
#define ON 1
#define OFF 0
/****************************************************************************
* 名 称: DelayMS()
* 功 能: 以毫秒为单位延时,系统时钟不配置时默认为16M(用示波器测量相当精确)
* 入口参数: msec 延时参数,值越大,延时越久
* 出口参数: 无
****************************************************************************/
void DelayMS(uint len)
{
while(len--)
for (int i=0; i<535; i++);
}
/****************************************************************************
* 名 称: LedOnOrOff()
* 功 能: 点亮或熄灭所有LED灯
* 入口参数: mode为1时LED灯亮 mode为0时LED灯灭
* 出口参数: 无
****************************************************************************/
void LedOnOrOff(uchar mode)
{
LED1 = mode;
LED2 = mode;
LED3 = mode;
}
/****************************************************************************
* 名 称: InitLed()
* 功 能: 设置LED相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitLed(void)
{
P1DIR |= 0x03; // P1.0、P1.1定义为输出
P0DIR |= 0x10; // P0.4定义为输出
LedOnOrOff(0); // 使所有LED灯默认为熄灭状态
}
/****************************************************************************
* 名 称: InitKey()
* 功 能: 设置按键相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitKey(void)
{
P0SEL &= ~0x02; //设置P0.1为普通IO口
P0DIR &= ~0x02; //按键接在P0.1口上,设P0.1为输入模式
P0INP &= ~0x02; //打开P0.1上拉电阻
}
/****************************************************************************
* 名 称: KeyScan()
* 功 能: 读取按键状态
* 入口参数: 无
* 出口参数: 0为抬起 1为按键按下
****************************************************************************/
uchar KeyScan(void)
{
if (KEY1 == 0)
{
DelayMS(10); //延时10MS去抖
if (KEY1 == 0)
{
while(!KEY1); //松手检测
return 1; //有按键按下
}
}
return 0; //无按键按下
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
InitLed(); //设置LED灯相应的IO口
InitKey(); //设置按键S1相应的IO口
while(1)
{
DelayMS(2);
if (KeyScan()) //扫描按键当前状态,按下按钮 松开 后执行
{
LED1 = ON; //点亮LED1
DelayMS(200);
LED1 = OFF; //熄灭LED1
LED2 = ON;
DelayMS(200);
LED2 = OFF;
LED3 = ON;
DelayMS(200);
LED3 = OFF;
}
}
}
2.3 外部中断
原理同上
/****************************************************************************
* 文 件 名: main.c
* 描 述: 通过按键S1产生外部中断改变LED1状态
****************************************************************************/
#include <ioCC2541.h>
#define uchar unsigned char
#define uint unsigned int
#define LED1 P1_0 // P1.0口控制LED1
#define KEY1 P0_1 // P0.1口控制S1
/****************************************************************************
* 名 称: DelayMS()
* 功 能: 以毫秒为单位延时,系统时钟不配置时默认为16M(用示波器测量相当精确)
* 入口参数: msec 延时参数,值越大,延时越久
* 出口参数: 无
****************************************************************************/
void DelayMS(uint len)
{
while(len--)
for (int i=0; i<535; i++);
}
/****************************************************************************
* 名 称: InitLed()
* 功 能: 设置LED灯相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitLed(void)
{
P1DIR |= 0x01; //P1.0定义为输出口
LED1 = 0; //LED1灯灭
}
/****************************************************************************
* 名 称: InitKey()
* 功 能: 设置KEY相应的IO口,采用中断方式
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitKey()
{
P0IEN |= 0x02; // P0.1 设置为中断方式 1:中断使能
PICTL |= 0x02; //下降沿触发
IEN1 |= 0x20; //允许P0口中断;
P0IFG = 0x00; //初始化中断标志位
EA = 1; //打开总中断
}
/****************************************************************************
* 名 称: P0_ISR(void) 中断处理函数
* 描 述: #pragma vector = 中断向量,紧接着是中断处理程序
****************************************************************************/
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{
DelayMS(10); //延时去抖
LED1 = ~LED1; //改变LED1状态
P0IFG = 0; //清中断标志
P0IF = 0; //清中断标志
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
InitLed(); //设置LED灯相应的IO口
InitKey(); //设置S1相应的IO口
while(1)
{
}
}
2.4 定时器 T1-查询方式
定时器T1的时钟频率为32MHz / 128 = 250kHz。
在这个配置下,当T1计数器溢出时,中断标志位IRCON会被置位。通过查询IRCON的值是否大于0,代码检测是否发生了定时器T1的中断。
在每次中断发生时,count会自增,并且当count累积到一定值时,LED1会翻转状态,实现LED的周期性闪烁。在这段代码中,count达到1时LED状态翻转,因此要实现约1秒的周期性闪烁,需要count累积到1。
/****************************************************************************
* 文 件 名: main.c
* 描 述: 定时器T1通过查询方式控制LED1周期性闪烁
****************************************************************************/
#include <ioCC2541.h>
#define uchar unsigned char
#define uint unsigned int
#define LED1 P1_0 // P1.0口控制LED1
/****************************************************************************
* 名 称: InitLed()
* 功 能: 设置LED灯相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitLed(void)
{
P1DIR |= 0x01; //P1.0定义为输出
LED1 = 0; //使LED1灯下电默认为熄灭
}
/****************************************************************************
* 名 称: InitT1()
* 功 能: 定时器初始化,系统不配置工作时钟时默认是2分频,即16MHz
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitT1()
{
T1CTL = 0x0d; //128分频,自动重装 0x0000-0xFFFF
T1STAT= 0x21; //通道0,中断有效
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
uchar count=0;
InitLed(); //调用初始化函数
InitT1();
while(1)
{
if(IRCON > 0)
{
IRCON=0;
if(count++ >= 1) //约1s周期性闪烁,示波器测大约为1025MS
{
count=0;
LED1 = !LED1; //LED1闪烁
}
}
}
}
2.5 定时器T3-中断方式
/****************************************************************************
* 文 件 名: main.c
* 描 述: 定时器T3通过中断方式控制LED1周期性闪烁
****************************************************************************/
#include <ioCC2541.h>
#define uchar unsigned char
#define uint unsigned int
#define LED1 P1_0 // P1.0口控制LED1
uint count; //用于定时器计数
/****************************************************************************
* 名 称: InitLed()
* 功 能: 设置LED灯相应的IO口
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitLed(void)
{
P1DIR |= 0x01; //P1.0定义为输出
LED1 = 0; //使LED1灯下电默认为熄灭
}
/****************************************************************************
* 名 称: InitT3()
* 功 能: 定时器初始化,系统不配置工作时钟时默认是2分频,即16MHz
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitT3()
{
T3CTL |= 0x08 ; //开溢出中断
T3IE = 1; //开总中断和T3中断
T3CTL |= 0xE0; //128分频,128/16000000*N=0.5S,N=62500
T3CTL &= ~0x03; //自动重装 00->0xff 62500/255=245(次)
T3CTL |= 0x10; //启动
EA = 1; //开总中断
}
//定时器T3中断处理函数
#pragma vector = T3_VECTOR
__interrupt void T3_ISR(void)
{
IRCON = 0x00; //清中断标志, 也可由硬件自动完成
if(count++ > 245) //245次中断后LED取反,闪烁一轮(约为0.5 秒时间)
{ //经过示波器测量确保精确
count = 0; //计数清零
LED1 = ~LED1; //改变LED1的状态
}
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
InitLed(); //设置LED灯相应的IO口
InitT3(); //设置T3相应的寄存器
while(1)
{};
}
2.6 串口通信--收发字符串
/****************************************************************************
* 文 件 名: main.c
* 描 述: 设置串口调试助手波特率:115200bps 8N1
* 串口调试助手给CC254x发字符串时,开发板会返回接收到的字符串
****************************************************************************/
#include <ioCC2541.h> //<ioCC2540.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
#define UART0_RX 1
#define UART0_TX 2
#define SIZE 51
char RxBuf;
char UartState;
uchar count;
char RxData[SIZE]; //存储发送字符串
/****************************************************************************
* 名 称: DelayMS()
* 功 能: 以毫秒为单位延时
* 入口参数: msec 延时参数,值越大,延时越久
* 出口参数: 无
****************************************************************************/
void DelayMS(uint len)
{
while(len--)
for (int i=0; i<535; i++);
}
/****************************************************************************
* 名 称: InitUart()
* 功 能: 串口初始化函数
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitUart(void)
{
PERCFG = 0x00; //外设控制寄存器 USART 0的IO位置:0为P0口位置1
P0SEL = 0x0c; //P0_2,P0_3用作串口(外设功能)
P2DIR &= ~0xC0; //P0优先作为UART0
U0CSR |= 0x80; //设置为UART方式
U0GCR |= 11;
U0BAUD |= 216; //波特率设为115200
UTX0IF = 0; //UART0 TX中断标志初始置位0
U0CSR |= 0x40; //允许接收
IEN0 |= 0x84; //开总中断允许接收中断
}
/****************************************************************************
* 名 称: UartSendString()
* 功 能: 串口发送函数
* 入口参数: Data:发送缓冲区 len:发送长度
* 出口参数: 无
****************************************************************************/
void UartSendString(char *Data, int len)
{
uint i;
for(i=0; i<len; i++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
/****************************************************************************
* 名 称: UART0_ISR(void) 串口中断处理函数
* 描 述: 当串口0产生接收中断,将收到的数据保存在RxBuf中
****************************************************************************/
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
URX0IF = 0; // 清中断标志
RxBuf = U0DBUF;
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
CLKCONCMD &= ~0x40; //设置系统时钟源为32MHZ晶振
while(CLKCONSTA & 0x40); //等待晶振稳定为32M
CLKCONCMD &= ~0x47; //设置系统主时钟频率为32MHZ
InitUart(); //调用串口初始化函数
UartState = UART0_RX; //串口0默认处于接收模式
memset(RxData, 0, SIZE);
while(1)
{
if(UartState == UART0_RX) //接收状态
{
if(RxBuf != 0)
{
if((RxBuf != '#')&&(count < 50))//以'#'为结束符,一次最多接收50个字符
RxData[count++] = RxBuf;
else
{
if(count >= 50) //判断数据合法性,防止溢出
{
count = 0; //计数清0
memset(RxData, 0, SIZE);//清空接收缓冲区
}
else
UartState = UART0_TX; //进入发送状态
}
RxBuf = 0;
}
}
if(UartState == UART0_TX) //发送状态
{
U0CSR &= ~0x40; //禁止接收
UartSendString(RxData, count); //发送已记录的字符串。
UartSendString("\r\n",2); //发送回车。
U0CSR |= 0x40; //允许接收
UartState = UART0_RX; //恢复到接收状态
count = 0; //计数清0
memset(RxData, 0, SIZE); //清空接收缓冲区
}
}
}
2.7 AD 采集 cc2530 温度串口显示
相关寄存器如下表所示
main.c
/****************************************************************************
* 文 件 名: main.c
* 描 述: ADC把芯片温度通过串口发给电脑,部分芯片误差较大需校准
* 手摸芯片温度有明显变化
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "UartTimer.h"
#define HAL_ADC_REF_1V25 0x00
#define ADC_12_BIT 0x30
#define ADC_EMP_SENS 0x0E
#define ADC_TO_CELSIUS(ADC_VALUE) ((ADC_VALUE>>4)-334) //温度校正
/****************************************************************************
* 名 称: InitSensor()
* 功 能: 温度传感器初始化函数
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitSensor(void)
{
DISABLE_ALL_INTERRUPTS(); //关闭所有中断
InitClock(); //设置系统主时钟为 32M
TR0=0x01; //设置为1来连接温度传感器到SOC_ADC
ATEST=0x01; //使能温度传感
}
/****************************************************************************
* 名 称: GetTemperature()
* 功 能: 获取温度传感器 AD 值
* 入口参数: 无
* 出口参数: 通过计算返回实际的温度值
****************************************************************************/
float GetTemperature(void)
{
unsigned int value;
unsigned char tmpADCCON3 = ADCCON3;
ADCIF = 0;
//选择1.25V为参考电压;12位分辨率;对片内温度传感器采样
ADCCON3 = (HAL_ADC_REF_1V25 | ADC_12_BIT | ADC_EMP_SENS);
while(!ADCIF); //等待 AD 转换完成
value = ADCL >> 2; //ADCL 寄存器低 2 位无效
value |= (((unsigned int)ADCH) << 6);
ADCCON3 = tmpADCCON3;
return ADC_TO_CELSIUS(value);
}
/****************************************************************************
* 程序入口函数
****************************************************************************/
void main(void)
{
char i;
float AvgTemp;
char strTemp[6];
InitUART(); //初始化串口
InitSensor(); //初始化 ADC
while(1)
{
AvgTemp = 0;
for (i=0; i<64; i++)
{
AvgTemp += GetTemperature();
}
AvgTemp = AvgTemp/64; //每次累加后除 64
memset(strTemp, 0, 6);
sprintf(strTemp,"%.02f", AvgTemp);//将浮点数转成字符串
UartSendString(strTemp, 5); //通过串口发给电脑显示芯片温度
UartSendString("\r\n",2);
DelayMS(1000); //延时
}
}
UartTimer.h
#include <ioCC2541.h>
typedef unsigned char uchar;
typedef unsigned int uint;
#define DISABLE_ALL_INTERRUPTS() (IEN0 = IEN1 = IEN2 = 0x00)//三个
void InitClock(void)
{
CLKCONCMD &= ~0x40; //设置系统时钟源为 32MHZ晶振
while(CLKCONSTA & 0x40); //等待晶振稳定
CLKCONCMD &= ~0x47; //设置系统主时钟频率为 32MHZ
}
/****************************************************************************
* 名 称: InitT3()
* 功 能: 定时器初始化
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitT3(void)
{
T3CCTL0 = 0x44; //T3CCTL0 (0xCC),CH0 中断使能,CH0 比较模式
T3CC0 = 0xFA; //T3CC0设置为250
T3CTL |= 0x9A; //启动T3计数器,计数时钟为16分频。使用MODULO模式
IEN1 |= 0x08;
IEN0 |= 0x80; //开总中断,开T3中断
}
/****************************************************************************
* 名 称: InitUart()
* 功 能: 串口初始化函数
* 入口参数: 无
* 出口参数: 无
****************************************************************************/
void InitUART(void)
{
PERCFG = 0x00; //位置1 P0口
P0SEL = 0x3C; //P0用作串口
P2DIR &= ~0xC0; //P0优先作为UART0
U0CSR |= 0x80; //串口设置为UART方式
U0GCR |= 11;
U0BAUD |= 216; //波特率设为115200
UTX0IF = 1; //UART0 TX中断标志初始置位1
U0CSR |= 0x40; //允许接收
IEN0 |= 0x84; //开总中断,接收中断
}
/****************************************************************************
* 名 称: UartSendString()
* 功 能: 串口发送函数
* 入口参数: Data:发送缓冲区 len:发送长度
* 出口参数: 无
****************************************************************************/
void UartSendString(char *Data, int len)
{
uint i;
for(i=0; i<len; i++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
U0DBUF = 0x0A; //输出换行
while(UTX0IF == 0);
UTX0IF = 0;
}
/****************************************************************************
* 名 称: DelayMS()
* 功 能: 以毫秒为单位延时 16M时大约为530,32M需要调整,系统时钟不修改默认为16M
* 入口参数: msec 延时参数,值越大,延时越久
* 出口参数: 无
****************************************************************************/
void DelayMS(uint msec)
{
uint i,j;
for (i=0; i<msec; i++)
for (j=0; j<1060; j++);
}