02_CC2530 + LED流水灯

CC2530 + LED流水灯

前言

​ 在搭建ZigBee定位系统前,先通过几个基础案例熟悉CC2530的一些外设寄存器编程方式。CC2530基础篇由LED流水灯(按键控制启停、定时器中断方式)定时器与Delay_ms延时函数Uart串口通信三章组成。

按键控制启停–通用I/O中断

硬件电路说明

​ 单片机程序的编写,需要考虑到开发板实际的电路设计。由于市面上的ZigBee开发板种类丰富,作者仅根据自己手中开发板进行讲解,所写代码仅供读者参考,但能保证在作者开发板上成功运行。本篇需要了解开发板上的LED电路按键电路

LED电路

​ 如下图所示,有D1D2D3POW四个LED灯,分别接在P1端口的1、2号引脚、P0端口的4号引脚和GND

其中POW灯一端接VDD3V3,另一端接GND,因此当板子上电即自动导通。D1D2D3仅当对应引脚设置输出低电平时导通。

在这里插入图片描述

KEY电路

​ 如下图所示,有S1S2S3三个按键,分别与CC2530P0_1(P0端口1号引脚)、P2_0以及RESET连接。当按键按下时,引脚与GND连接,会产生一个下降沿(高电平—>低电平)。

在这里插入图片描述

I/O端口

CC2530有21个数字输入/输出引脚,可以配置为通用数字I/O外设I/O信号,配置为连接到ADC、定时器或 USART 外设。这些 I/O 口的用途可以通过一系列寄存器配置,由用户软件加以实现。

通用I/O

​ 用作通用 I/O 时,引脚可以组成 3 个 8 位端口,端口 0、端口 1 和端口 2,表示为 P0、P1 和 P2。其中,P0 和 P1 是完全的 8 位端口。

​ 要使用通用I/O功能,需要用到以下寄存器:

  • 所有的端口均可以通过 SFR(特殊功能寄存器): P0、P1 和 P2 直接寻址和字节寻址
  • 寄存器 PxSEL,其中 x 为端口的标号 0~2,用来设置端口的每个引脚为通用 I/O 或者是外部设备 I/O 信号。
  • 寄存器 PxDIR 来设置每个端口引脚为输入或输出
  • 用作输入时,通用 I/O 端口引脚可以设置为上拉、下拉或三态操作模式。复位之后,所有的端口均设置为带上拉的输入。要取消输入的上拉或下拉功能,就要将 PxINP 中的对应位设置为 1

下面是P0端口相关的寄存器: (直观了解)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通用I/O中断

​ 通用 I/O 引脚设置为输入后,可以用于产生中断。中断可以设置在外部信号的上升或下降沿触发。P0、P1 或 P2 端口都有中断使能位。控制端口中断使能的寄存器如下:

  • IEN1.P0IE: P0中断使能

  • IEN2.P1IE: P1中断使能

  • IEN2.P2IE: P2中断使能

    控制位中断使能寄存器如下:

  • PxIEN: Px中断使能, x为0-2

    此外还有用于保存中断标志触发沿设置的寄存器:

  • PICTL: P0、P1和P2触发沿设置

  • PxIFG: Px中断标志, x为0-2

    了解完通用I/O中断,还不足以成功使用配置中断。事实上,在CC2530中,配置中断有一个基本的固定流程,将在下一小节进行介绍。

中断屏蔽

在这里插入图片描述

​ 根据CC2530数据手册,为了使能任一中断,有以上基本流程。

具体代码讲解

LED.h
#ifndef __LED_H
#define __LED_H
#define LED1 P1_0       // 定义LED1 为P1_0
#define LED2 P1_1		// 定义LED2 为P1_1
#define LED3 P0_4		// 定义LED3 为P0_4
// 函数声明
void LED_Init(void);
void LED_Flow(void);
void LED_OFF(void);
#endif
LED.c
#include <ioCC2530.h>    // CC2530头文件 里头是各种寄存器的定义
#include "LED.h"
#include "delay.h"
/**
* @brief 初始化LED
*/
void LED_Init(void)
{P1SEL &= ~0x03; P0SEL &= ~0x10;P1DIR |= 0x03;  // P1_0 P1_1 设置为输出P0DIR |= 0x10;  // P0_4 设置为输出LED1 = 1;LED2 = 1;LED3 = 1;
}/**
* @brief LED流水灯
*/
void LED_Flow(void)
{LED3 = !LED3;  // 转换状态Delay_ms(5000);  // 延时LED2 = !LED2;Delay_ms(5000);LED1 = !LED1;Delay_ms(5000);
}/**
* @brief 关闭所有LED
*/
void LED_OFF(void)
{LED1 = 1;LED2 = 1;LED3 = 1;
}
LED_Init()函数详解:
P1SEL &= ~0x03;  // &= 11111100(B)  实际是将P1的0号引脚和1号引脚的功能选择位置0,即选择通用I/O功能

在这里插入图片描述

P0SEL &= ~0x10;  // &= 11101111(B)  将P0的4号引脚的功能选择位置零, 同上

在这里插入图片描述

P1DIR |= 0x03;   // |= 00000011  将P1的0号和1号引脚设置为输出模式

在这里插入图片描述

P0DIR |= 0x10;  // |= 00010000  将P0的4号引脚设置为输出模式,同上不放寄存器的表了
LED1 = 1;   // 设置为高电平 关灯 由于硬件是低电平驱动
LED2 = 1;	// 默认关灯
LED3 = 1;	// 默认关灯
  • Note: 要向用CC2530的外设功能,首先需要根据其数据手册,找到相应寄存器,并正确配置。以后的章节中,将不一一对照代码找到寄存器表,仅说明配置的流程以及相应的寄存器。因为寄存器数量太多,全部找出来放上去不如直接翻数据手册。此次找出是为了让读者直观体会到裸机开发寄存器的重要性,以及LED驱动代码所涉及的寄存器较少,比较适合用于教程。值得注意的是,在以后所有的CC2530开发中,基本流程都与LED流水灯类似,无非就是外设不同用到的寄存器不同,功能越复杂的外设,其需要配置的寄存器更多。
delay.c
#include "delay.h"void Delay_ms(unsigned int time)   // 实际上不精确
{unsigned char n;while(time > 0){for(n=0; n<187;++n){asm("nop");}time--;}
}

Delay_ms()函数,延时1ms,通过循环执行空指令达到延时目的,并不准确,后续在定时器与Delay函数的章节中,会有准确的Delay_ms()函数实现。

delay.h
#ifndef __DELAY_H
#define __DELAY_H
void Delay_ms(unsigned int time);
#endif
Key.c
#include <ioCC2530.h>
#include "Key.h"
#include "delay.h"
unsigned int key_flag = 0;
/**
* @brief 按键初始化函数 开启P0_1 外部中断
*/
void Key_Init(void)
{P0IF = 0;  // 1. 清除中断标志P0IE = 1;  // 2. 使能P0中断  IEN1寄存器P0IEN |= 0x02;  // 3. 使能P0_1 中断 SFR寄存器EA = 1;  // 4. 使能全局中断PICTL |= 0x02;  // 配置P0 下降沿触发中断   return;
}/**
* @brief P0 中断服务函数  5.
*/
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{if(P0IFG > 0)  // P0IFG是端口0 的标志寄存器 共八位 对应引脚 7-0{P0IFG = 0x00;   // 清除标志位Delay_ms(50);   // 延时用于按键消抖key_flag = ~key_flag;  // 按下按钮后状态 按键状态改变}P0IF = 0;  // 清除 标志位 IRCON寄存器中 这个寄存器汇总了外设 端口0 定时器1-4 以及DMA中断标志等
}

​ 从按键初始化代码中可以看出,要使能一个中断,应遵循上一节的五个基本步骤(具体看中断屏蔽)。

  • Note: 值得一提的是 对于CC2530的中断服务函数有其独特的写法
#pragma vector = <中断向量>
__interrupt void <函数名>(void)
{函数体
}

​ 其中的中断向量,定义在<ioCC2530.h>文件中,如下图所示

在这里插入图片描述

Key.h
#ifndef __KEY_H
#define __KEY_H
#define KEY1 P0_1
extern unsigned int key_flag;
void Key_Init(void);
#endif
main.c
#include <ioCC2530.h>
#include "LED.h"
#include "key.h"void main(void)
{LED_Init();  // LED初始化Key_Init();  // 按键初始化while(1){if(key_flag == 0)LED_OFF();  // 关灯elseLED_Flow();  // 流水灯}
}

主函数中,先初始化LED按键, 然后根据按键状态,开始流水灯或者关掉所有LED灯。

定时器中断方式

​ 由于单纯用循环执行空指令的延时函数并不精确,为了更加精确地控制流水灯闪烁,方法之二是利用定时器功能实现延时函数直接利用定时器中断改变LED灯的状态

定时器T1

定时器T1包括一个16位计数器,当32MHz晶振用作系统时钟源时,定时器T1可以使用的最低时钟频率是1953.125Hz,最高是32 MHz。当16MHz RC 振荡器用作系统时钟源时,定时器T1可以使用的最高时钟频率是16MHz。

​ 可以通过两个8位的SFR读取16位的计数器值:

  • T1CNTH: 计数器值高位字节
  • T1CNTL: 计数器值低位字节
  • 值得注意的是: 当读取T1CNTL时,计数器的高位字节在那时被缓冲到T1CNTH,以便高位字节可以从T1CNTH中读出。因此T1CNTL 必须总是在读取T1CNTH之前首先读取。
模模式

​ 当定时器运行在模模式,16位计数器从0x0000开始,每个活动时钟边沿增加 1。当计数器达到 T1CC0(溢出),寄存器 T1CC0H:T1 CC0L 保存的最终计数值,计数器将复位到 0x0000,并继续递增。如果设置了相应的中断屏蔽位 TIMIF.OVFIM 以及 IEN1.T1EN,将产生一个中断请求。

​ 使用定时器T1模模式,需要配置以下寄存器:

  • T1CTL: 定时器控制寄存器
  • T1CC0HT1CC0L: 保存最终的计数值
  • T1CCTL0 : 定时器1通道0 捕获/比较控制,使用定时器1模计数时,必须使能 T1CCTL0.MODE =1
  • TIMIF: 定时器 1 /3/4 联合中断屏蔽/标志
  • T1EN: 定时器1中断使能
时钟配置

​ 定时器的工作需要时钟源,系统时钟是从所选的主系统时钟源获得的,主系统时钟源可以是32MHz XOSC或16MHz RCOSC。 在确定系统时钟频率定时器预分频值以及计数器最终计数值(重装值)后,即可确定每发生一次定时器溢出中断函数经过的时间

  • CLKCONCMD.OSC 位选择主系统时钟的源。

  • CLKCONSTA: 时钟控制状态

具体代码讲解
工程结构

在这里插入图片描述

Clock.c
#include <ioCC2530.h>
#include "Clock.h"/**
* @brief 设置系统时钟为32Mhz晶振
*/
void Oclk32_Init(void)
{CLKCONCMD &= ~0x40;        // 设置系统时钟源为 32MHZ晶振while(CLKCONSTA & 0x40); // 等待晶振稳定 CLKCONCMD &= ~0x3f;      //    定时器tick输出设置   设置系统主时钟频率为 32MHZ
}/**
* @brief 设置系统时钟为16Mhz晶振
*/
void Oclk16_Init(void)
{CLKCONCMD |= 0x40;while(!(CLKCONSTA & 0x40));CLKCONCMD |= 0x09;CLKCONCMD &= 0xC9;
}
Clock.h
#ifndef __CLOCK_H
#define __CLOCK_H
void Oclk32_Init(void);
void Oclk16_Init(void);
#endif
timer1.c
#include "timer1.h"
#include "LED.h"
#include <ioCC2530.h>
unsigned int time = 0;
/**
* @brief 定时器1初始化函数 定时器中断 
*/
void Timer1_Init(void)
{// 开启定时器1中断T1IF = 0;  // 清除中断标志T1STAT = 0x00;   // 清除中断标志T1IE = 1;  // 定时器1中断使能  IEN1T1OVFIM = 1;  // 定时器1 溢出中断屏蔽位EA = 1;  // 使能全局中断// 定时器1参数配置  当系统时钟为32Mhz时1ms中断1次  32 000 000/ 32 / 1000 = 1000Hz = 1msT1CTL |= 0x0A; // 预分频 32  模模式T1CCTL0 |=0X04; //使用模计数时,必须使能 T1CCTL0.MODE =1T1CC0H = 0x03; // 最终计数值 设为1000  高位寄存器T1CC0L = 0xE8;  // 低位寄存器
}// 定时器T1中断服务函数
#pragma vector = T1_VECTOR
__interrupt void T1_ISR(void)
{IRCON = 0x00;  // 清除中断标志位if(time++ >= 1000)  // 每进入中断1次 time加1  当time为1000时(约等于1000ms=1s)为真{time = 0;   // 清除 timeif(LED_Flag == 0){LED1 = ~LED1;}else if(LED_Flag == 1){LED2 = ~LED2;}else if(LED_Flag == 2){LED3 = ~LED3;}LED_Flag = (LED_Flag+1)%3;  // 切换灯光}
}
main.c
#include <ioCC2530.h>
#include "LED.h"
#include "timer1.h"
#include "Clock.h"
void main(void)
{Oclk32_Init();  // 系统时钟初始化 32MHZLED_Init();    // LED初始化Timer1_Init(); // 定时器1初始化while(1){// 由于灯光切换在中断函数中实现,主函数初始化后死循环即可}
}
  • Note: LED相关代码与按键控制启停小节一致

结语

​ 本章介绍了CC2530通用I/O通用I/O中断以及定时中断的应用案例。简单使用了I/O端口定时器两大基本外设,并演示了寄存器编程方式(见LED_Init()函数详解),值得注意的是在LED_Init()函数讲解中,为了讲解清楚,作者按照的逻辑是根据代码讲解对应寄存器,但实际编程时,应按照根据所用寄存器写代码的逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/3412.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

无线模块的最佳搭档:天线全面选型指南

在无线通信领域&#xff0c;天线的选择至关重要。它不仅影响信号的覆盖范围和传输质量&#xff0c;还直接关系到系统的整体性能。在众多无线模块中&#xff0c;找到合适的天线可以最大化其潜力&#xff0c;确保稳定和高效的数据传输。 在设计适用于射频系统的无线收发设备时&a…

产品思维笔记(一):打造用户喜爱的产品by Marty Cagan

全文摘要 《启示录&#xff1a;打造用户喜爱的产品》是由美国著名产品经理Marty Cagan所著&#xff0c;他曾经是eBay最出色的产品经理之一&#xff0c;也是Google X实验室的创始人之一。在这本书中&#xff0c;他分享了自己的经验和教训&#xff0c;帮助读者更好地理解如何打造…

推荐一款功能强大的电影格式转换器:Total Movie Converter

Coolutils Total Movie Converter(电影格式转换器)是一款可以将超清或者高清蓝光的视频电影进行格式转换的工具&#xff0c;高质量速度快操作简单就是软件最大的亮点&#xff0c;它可以转换几乎所有流行的视频编解码器。 基本简介 Coolutils Total Movie Converter 也可以使视…

掌声响起来——不确定性人工智能与高斯云方法的应用

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

P11232 [CSP-S 2024] 超速检测

P11232 [CSP-S 2024] 超速检测 难度&#xff1a;普及/提高。 考点&#xff1a;二分、贪心。 题意&#xff1a; 题意较长&#xff0c;没有题目大意&#xff0c;否则你也大意。 主干道长度为 L L L&#xff0c;有 n n n 辆车&#xff0c;看做左端点为 0 0 0&#xff0c;第 …

JSP九大内置对象和四大作用域

get和post区别&#xff1a; 比较项 get post 缓存 可以 不可以 收藏为书签 可以 不可以 数据长度 有限制&#xff08;URL 的最大长度是 2048 个字符&#xff09; 无限制 编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multi…

【java batik_使用BATIK解析SVG生成PNG图片】

矢量图的介绍及应用场景 矢量图是什么意思&#xff1f; 矢量图&#xff0c;也称为向量图&#xff0c;英文名字是Vector graphics。 矢量图是一种基于矢量的图形&#xff0c;由一系列的线段和曲线组成。由数学公式和算法生成的。这意味着矢量图可以在任何分辨率下清晰地显示&…

针对物联网边缘设备基于EIT的手部手势识别的1D CNN效率增强的组合模型压缩方法

论文标题&#xff1a;Combinative Model Compression Approach for Enhancing 1D CNN Efficiency for EIT-based Hand Gesture Recognition on IoT Edge Devices 中文标题&#xff1a;针对物联网边缘设备基于EIT的手部手势识别的1D CNN效率增强的组合模型压缩方法 作者信息&a…

0.STM32F1移植到F0的各种经验总结

1.结构体的声明需放在函数的最前面 源代码&#xff1a; /*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructu…

【AIGC】ChatGPT提示词Prompt高效编写技巧:逆向拆解OpenAI官方提示词

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;OpenAI官方提示词的介绍OpenAI官方提示词的结构与组成如何通过分析提示词找到其核心组件 &#x1f4af;OpenAI官方提示词分析案例一&#xff1a;制定教学计划案例二&…

Leetcode 62. 不同路径 动态规划+空间优化

原题链接&#xff1a;Leetcode 62. 不同路径 二维数组&#xff1a; class Solution { public:int uniquePaths(int m, int n) {int res 0;int box[m][n];for (int i 0; i < m; i) {box[i][0] 1;}for (int j 0; j < n; j) {box[0][j] 1;}for (int i 1; i < m;…

app的登录破解 frida jadx

今天收到了一个APP让我研究一下登录 登录已经研究完成 下面则是我的整体思路 为了安全考虑这个app我就不说是那个了 我就说整体的思路 仅供交流学习 严谨非法使用开始进行抓包 手机使用代理连接charles 之后开始点击app登录 进行抓包下面则是我抓到的包 抓包之后j进行改包 也…

【IEEE/CCF-C类】1区顶刊变水刊?发文量暴涨1600+,光速审稿,圆你顶刊梦!

&#x1f525; &#x1f525; &#x1f525; &#x1f525; 本期小编解析的是一本由IEEE旗下多个学会联合出版的计算机领域的TOP期刊《IEEE Internet of Things Journal》&#xff0c;该期刊自2014年创刊&#xff0c;专注于物联网&#xff08;IoT&#xff09;领域的研究…

django高校学生信息管理系统-计算机毕业设计源码02553

django高校学生信息管理系统 摘 要 本研究旨在设计和实现基于Django框架的高校学生信息管理系统&#xff0c;涵盖了系统用户、学生信息管理、教师信息管理、课程分类管理、开课信息管理、选课信息管理、课表信息管理、成绩信息管理、系统管理、网站公告管理和校园资讯等多个功能…

特殊矩阵的压缩存储

一维数组的存储结构 ElemType arr[10]; 各数组元素大小相同&#xff0c;且物理上连续存放。 数组元素a[i]的存放地址 LOC i * sizeof(ElemType)。&#xff08;LOC为起始地址&#xff09; 二维数组的存储结构 ElemType b[2][4];二维数组也具有随机存取的特性&#xff08;需…

中立性DEA交叉效率评价方法

今天推出中立性DEA模型的计算工具 参考文献&#xff1a;《中立性DEA交叉效率评价方法》袁剑波&#xff0c;吴立辉&#xff0c;魏思 中立性DEA交叉效率评价方法 在数据包络分析&#xff08;DEA&#xff09;对决策单元效率评价的方法中&#xff0c;对抗性DEA交叉效率方法把所有…

【Visual Studio】解决 CC++ 控制台程序 printf 函数输出中文和换行符显示异常

问题描述 C&C 控制台程序 printf 函数输出中文和换行符 \n 显示异常。 #include <stdio.h>int main() {int choice;printf("菜单:\n");printf("1. 选项一\n");printf("2. 选项二\n");printf("3. 选项三\n");printf("…

【dvwa靶场:XSS系列】XSS (Stored)低-中-高级别,通关啦

更改name的文本数量限制大小&#xff0c; 其他我们只在name中进行操作 【除了低级可以在message中进行操作】 一、低级low <script>alert("假客套")</script> 二、中级middle 过滤了小写&#xff0c;咱们可以大写 <Script>alert("假客套…

【Linux】深入理解进程控制:从创建到终止和进程等待

文章目录 进程创建fork函数如何用fork函数创建子进程写实拷贝 进程终止错误信息exit_exit 进程等待waitwaitpid 总结 进程创建 fork函数 fork 函数是 Unix/Linux 系统中用于创建新进程的系统调用。调用 fork 后&#xff0c;当前进程&#xff08;父进程&#xff09;会被复制&a…

【java】对象的内存存储

目录 对象在内存中的分配设计到的内存结构(理论)类中对象的内存解析创建类的一个对象&#xff0c;属性赋值创建类的多个对象&#xff0c;属性赋值 对象在内存中的分配设计到的内存结构(理论) 栈&#xff1a;方法内定义的变量&#xff0c;存储在栈中 堆&#xff1a;new出来的结…