Data sheet;
芯片的所有数据可从官方数据手册中查看,如IC的Pin 定义、电器特性、机械特性、料号定义等,如下在芯片STM32F103x8B
的数据手册中查看其型号为LQFP100的 100个引脚的物理分布和定义:
Reference Manual;
对芯片的外设的具体描述和功能介绍;
Errata Sheet;
描述IC某些功能的局限性(硬件bug)并给出解决方法;
官方的STM32内核手册;
Application Note;
官方的针对不同应用场合的描述性文档,一般配套有固件例程;
http://www.openedv.com/docs/index.html#
CMSIS(Cortex Microcontroller Software Interface Standard - 微控制器软件接口标准)是ARM 官方设计的驱动包;
因为ST 公司设计的Cortex 系列芯片采用的同一内核,只是核外的片上外设不同,这些差异导致在相同内核不同外设的芯片上移植工作变得非常困难,为了解决不同芯片厂商生产的Cortex 内核芯片的软件兼容性问题,ARM 与芯片厂商建立了CMSIS 标准;CMSIS 标准实际是新建了一个软件抽象层,STM32 的固件库就是按照CMSIS 建立起来的;
CMSIS软件包的获取途径:
CMSIS的组成框图:
如下图:
内核函数层: 用于访问内核寄存器的名称、地址定义,主要由ARM公司提供;
设备外设访问层: 这一层提供了片上的核外外设地址和中断定义,主要由芯片生产商提供。
ST 公司为了方便开发人员,对寄存器进行了封装并发布了STM32固件库(Firmware Library),它是一个固件函数包,它由程序、数据结构和宏组成,包括了微控制器所有外设的性能特征;
固件库中的库函数包括了每一个外设的驱动描述和应用实例,并为开发者访问底层硬件提供了一个中间API ( application program interface )通过使用固件函数库,无需深入掌握底层硬件细节,开发者就可以轻松应用每一个外设;
使用标准外设库进行开发的最大优势在于可以使开发者不用深入了解硬件底层细节,就可以灵活规范的使用每一个外设;
固件库函数本质上是为用户提供一种可读性强的寄存器配置方法,是架设在寄存器与用户驱动层之间的接口,向下完成寄存器的配置,向上为用户提供可读性强的功能函数;
在官网获取对应芯片的固件库解压并打开后,其文件夹树如下图所示:
下面着重介绍Libraries 文件夹下的两个文件夹:
Stm32f10x.h
这个头文件,实现了片上外设的所有寄存器的映射,是一个非常重要的头文件;system_stm32f10x.c
文件和它下面的头文件实现了STM32的时钟配置,操作的是片上的RCC 这个外设;stm32f10x_gpio.c
及stm32f10x_gpio.h
文件为例,如果在开发的工程中用到了STM32的GPIO 外设,就至少要把这两个文件包含到工程里。这里函数对应的就是每个功能外设的驱动函数,因此它也是ST 标准库函数的主要内容;stm32f10x_it.c
这个文件是专门用来编写中断服务函数的,在我们对它修改前,这个文件已经定义了一些系统异常中断的接口,其它普通中断服务函数由我们自己添加;system_stm32f10x.c
这个文件包含了STM32芯片上电后初始化系统时钟扩展外部存储器使用的函数;stm32f10x_conf.h
这个文件被包含进stm32f10x.h
文件,它的作用同意管理外设的所有头文件,在需要的时候进行对应取消注释即可,参考:CSDN 博客:stm32f10x_conf.h;在一些比较新的或高性能的芯片中会带有HAL库
HAL(Hardware Abstraction Layer - 硬件抽象层)库包含在芯片的软件包内,是芯片的外设驱动包,代码文件路径为(以STM32H7为例):Drivers\STM32H7xx_HAL_Driver;每个源文件开头都带有使用说明;
stdint.h
有明确定义: /* exact-width signed integer types */
typedef signed char int8_t; // 将有符号字符型 signed char 定义为 int8_t
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed __INT64 int64_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t; // 将无符号字符型 unsigned char 定义为 uint8_t
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __INT64 uint64_t;
/* 7.18.1.2 */
/* smallest type of at least n bits */
/* minimum-width signed integer types */
typedef signed char int_least8_t;
typedef signed short int int_least16_t;
typedef signed int int_least32_t;
typedef signed __INT64 int_least64_t;
/* minimum-width unsigned integer types */
typedef unsigned char uint_least8_t;
typedef unsigned short int uint_least16_t;
typedef unsigned int uint_least32_t;
typedef unsigned __INT64 uint_least64_t;
/* 7.18.1.3 */
/* fastest minimum-width signed integer types */
typedef signed int int_fast8_t;
typedef signed int int_fast16_t;
typedef signed int int_fast32_t;
typedef signed __INT64 int_fast64_t;
/* fastest minimum-width unsigned integer types */
typedef unsigned int uint_fast8_t;
typedef unsigned int uint_fast16_t;
typedef unsigned int uint_fast32_t;
typedef unsigned __INT64 uint_fast64_t;
/* 7.18.1.4 integer types capable of holding object pointers */
#if __sizeof_ptr == 8
typedef signed __INT64 intptr_t;
typedef unsigned __INT64 uintptr_t;
#else
typedef signed int intptr_t;
typedef unsigned int uintptr_t;
#endif
/* 7.18.1.5 greatest-width integer types */
typedef signed __LONGLONG intmax_t;
typedef unsigned __LONGLONG uintmax_t;
stdint.h
中也有对数据类型的范围定义:/* minimum values of exact-width signed integer types */
#define INT8_MIN -128
#define INT16_MIN -32768
#define INT32_MIN (~0x7fffffff) /* -2147483648 is unsigned */
#define INT64_MIN __INT64_C(~0x7fffffffffffffff) /* -9223372036854775808 is unsigned */
/* maximum values of exact-width signed integer types */
#define INT8_MAX 127
#define INT16_MAX 32767
#define INT32_MAX 2147483647
#define INT64_MAX __INT64_C(9223372036854775807)
/* maximum values of exact-width unsigned integer types */
#define UINT8_MAX 255
#define UINT16_MAX 65535
#define UINT32_MAX 4294967295u
#define UINT64_MAX __UINT64_C(18446744073709551615)
/* 7.18.2.2 */
/* minimum values of minimum-width signed integer types */
#define INT_LEAST8_MIN -128
#define INT_LEAST16_MIN -32768
#define INT_LEAST32_MIN (~0x7fffffff)
#define INT_LEAST64_MIN __INT64_C(~0x7fffffffffffffff)
/* maximum values of minimum-width signed integer types */
#define INT_LEAST8_MAX 127
#define INT_LEAST16_MAX 32767
#define INT_LEAST32_MAX 2147483647
#define INT_LEAST64_MAX __INT64_C(9223372036854775807)
/* maximum values of minimum-width unsigned integer types */
#define UINT_LEAST8_MAX 255
#define UINT_LEAST16_MAX 65535
#define UINT_LEAST32_MAX 4294967295u
#define UINT_LEAST64_MAX __UINT64_C(18446744073709551615)
/* 7.18.2.3 */
/* minimum values of fastest minimum-width signed integer types */
#define INT_FAST8_MIN (~0x7fffffff)
#define INT_FAST16_MIN (~0x7fffffff)
#define INT_FAST32_MIN (~0x7fffffff)
#define INT_FAST64_MIN __INT64_C(~0x7fffffffffffffff)
/* maximum values of fastest minimum-width signed integer types */
#define INT_FAST8_MAX 2147483647
#define INT_FAST16_MAX 2147483647
#define INT_FAST32_MAX 2147483647
#define INT_FAST64_MAX __INT64_C(9223372036854775807)
/* maximum values of fastest minimum-width unsigned integer types */
#define UINT_FAST8_MAX 4294967295u
#define UINT_FAST16_MAX 4294967295u
#define UINT_FAST32_MAX 4294967295u
#define UINT_FAST64_MAX __UINT64_C(18446744073709551615)
...
架构:架构(Architecture)即框架结构的意思,以ARM 架构为例,ARM 架构就是由英国ARM 公司设计的一系列32位的RISC 微处理器架构总称,现有ARMv1~ARMv8种类;
内核:内核/核心(Core);
架构与内核的理解:以盖房子为例,平房、三层小洋楼、高层公寓、摩天大楼,这些基础部分为架构;而架构内布置设计,如三室两厅带双卫的设计图纸A即为内核;
架构与内核的关系就是,平房内可根据设计图纸A 布置空间,也可以根据图纸B 布置空间,三层小洋房也可以根据图纸A/B/C 布置空间,高层公寓也可以根据图纸A/B/C/D… 布置很多个空间;根据需求不同,会产生出不同的内核去满足需求;
malloc
、calloc
、 realloc
等函数分配的变量空间是在堆上。以 STM32H7 为例,堆栈是在 startup_stm32h743xx.s
文件里面设置:STM32 的系统存储区自带 bootloader程序,此程序是 ST 在芯片出厂时烧录进去的,主要用于将用户应用程序下载到芯片内部 Flash;支持 USB,SPI,I2C,CAN,UART 等接口方式下载。
单片机烧录的方式:
Bootloader程序升级流程:
STM32芯片的内核由ARM 设计,而FLASH、SRAM、寄存器组等存储空间却是ST 设计的,它们本身并不具备地址信息,而给这些存储器分配地址的过程就叫存储器映射;
STM32芯片的这些存储器空间,被组织在同一个容量为4G (0000 0000 ~ FFFF FFFF)的线性地址空间内,如下图:
STM32的片上外设由总线控制,它们分别挂载在了APB 和AHB 两个总线上,其中APB 挂载低速外设、AHB 挂载高速外设;
其中APB、AHB 总线又进一步划分为APB1、APB2和AHB1、AHB2;
如上示意图:
((unit32_t)0x4000 0000)
;#define PERIPH_BASE ((uint32 t)0x40000000) // 片上外设总线的基地址
0x10000
#define APB2PERIPH_BASE (PERIPH BASE + 0x10000) // APB2总线的基地址
Ox0800
#define GPIOA_BASE (APB2PERIPH BASE + Ox0800)
以上述原理,即可准确定位STM32片上外设的具体地址;
计算机系统的三大平台:服务器、桌面计算机、嵌入式;
嵌入式系统的一般定义:以应用为中心、以计算机技术为基础,软硬件可裁剪,对功能、可靠性、成本、体积、功耗和应用环境有特殊要求的专用计算机系统,是将应用程序、操作系统和计算机硬件集成在一起的系统。
嵌入式系统的系统分级:片级(芯片)、板级(主板)、系统级;
STM32 的所有IO 口都可作为中断输入;
STM32的大多数IO 口都是5V 兼容的,而IO 口的5V 兼容性可以通过数据手册查询,标记有FT 的就是5V 兼容的;
STM32引脚是用来做控制而不是做驱动使用的;
以STM32F103VET6 芯片为例,其一共有5组IO 口,每组IO 口有16个IO,它们分别是:
电压与电平:
V
表示;0
或1
表示,一般有明确的电压区间定义;VDD,VCC,VSS,GND,地之间有何区别?
更多关于STM32 GPIO 的介绍,可查看《STM32中文参考手册》第8章通用和复用功能IO(GPIO 和AFIO);
关于STM32 GPIO 的Pin 定义,可查看对应芯片的Datasheet;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能复用的片上外设功能时钟
//USART1_TX PA.9 复用推挽输出配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10 浮空输入配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能要重映射的内置外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能端口复用时钟
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE) // 使能串口1重映射功能
GPIO_PinRemapConfig
函数的第一个入口参数的取值范围了解到;在 stm32f10x_gpio.h
文件中定义了取值范围为下面宏定义的标识符,这里贴一小部分:#define GPIO_Remap_SPI1 ((uint32_t)0x00000001)
#define GPIO_Remap_I2C1 ((uint32_t)0x00000002)
#define GPIO_Remap_USART1 ((uint32_t)0x00000004)
#define GPIO_Remap_USART2 ((uint32_t)0x00000008)
#define GPIO_PartialRemap_USART3 ((uint32_t)0x00140010)
#define GPIO_FullRemap_USART3 ((uint32_t)0x00140030)
从上代码可知,USART1 、USART2只有一种重映射,而 USART3 存在部分重映射和完全重映射;
以下是手册中的 USART3 重映射表:
可见,部分重映射就是将 PB10、PB11、PB12 重映射到 PC10、PC11、PC12 上,而 PB13 和 PB14引脚位置不变。完全重映射就是将这两个脚也重新映射到 PD11 和 PD12 去。
GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE); // 串口3部分重映射
GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE); // 串口3完全重映射
TTL 施密特触发器的基本原理:当输入电压高于正向闯值电压,输出为高;当输人电压低于负向闯值电压,输出为低;lO 口信号经过触发器后,模拟信号转化为0和1的数字信号,即高低电平;
输入浮空下,模拟信号从IO 口进入,上下拉电阻开关都断开(浮空状态),通过TTL 施密特触发器把模拟信号转换为数字信号,再到IDR 寄存器,CPU 再读取寄存器;
浮空输入状态下,STM32读到的电平只根据IO 端口的电平有关;
注:在MCU 程序内对应GPIO 引脚配置为输入浮空模式下,当外部无输入信号且无外部上/下拉电阻时,MCU GPIO 此时呈现高阻态,其电压状态处于不确定状态(具体表现为使用万用表测量,可能只有一点伏,而且上下跳动不稳);
输入上拉与浮空输入的区别在于,输入上拉模式下上拉电阻(约30k~50k ohm)开关接通,外部信号电平通过上拉电阻被拉高到3.3V,即当外部无信号输入(悬空)时,IO 口默认为高电平;
输入下拉与浮空输入的区别在于,输入下拉模式下下拉电阻(约30k~50k ohm)开关接通,外部信号电平通过下拉电阻被拉低到GND,即当外部无信号输入(悬空)时,IO 口默认为低电平;
模拟输入模式下,模拟信号从IO 口进入,上下拉电阻开关都断开,TTL 施密特触发器截止,模拟信号直接输入到片上ADC,此时IDR 寄存器为空,CPU 不能在IDR 寄存器上读到引脚状态;
以下表所示,如供电电压VDD = 3.3V,高于0.7VDD = 2.31V 即判定为高电平,低于0.3VDD = 0.99V 即判定为低电平;
CPU 先将输出值通过设置BSRR 寄存器和ODR 寄存器写人输出电平值,信号通过输出控制电路和N-Mos 到达输出端口,形成开漏输出;
- 此模式下P-MOS 管不工作;
- 开漏输出的驱动能力比推挽输出弱,即该模式下输出电流较小;
1
时,N-MOS 管截止,输出呈高阻态,输出的实际信号由IO 口的上下拉电阻决定,其实际输出的电平信号可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;0
时,N-MOS 管导通,输出电平信号被N-MOS 管拉到Vss(公共地),IO 口输出低电平0
,同理该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;@_@
开漏复用输出与开漏输出的区别在于其信号来源于片上外设模块而不是CPU;
推挽输出与开漏输出的区别在于推挽输出模式下输出驱动器的P-MOS 管正常工作,即P-MOS 和N-MOS 管同时工作;
MCU 首先将输出值通过设置BSRR 寄存器和ODR 寄存器写入输出电平值,信号通过输出控制电路和P-MOS 和N-Mos 管到达输出端口,形成推挽输出;
Case1:CPU 控制IDR 寄存器输出1
时,P-MOS 管导通,输出电平信号被P-MOS 管拉到Vdd(器件工作电压),IO 口输出高电平1
,该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;
Case2:与推挽输出模式同理,CPU 控制IDR 寄存器输出0
时,N-MOS 管导通,输出电平信号被N-MOS 管拉到Vss(公共地),IO 口输出低电平0
,同理该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;
- 当切换输入高低电平时,两个MOS管将轮流导通,一个负责拉电流,电流输入到负载,一个负责灌电流,负载电流流向芯片,使其带负载能力和开关速度都比普通的方式有很大的提高。
- 与开漏输出相比,推挽输出模式可以输出强高低电平,适合连接数字器件,开关速度快,带负载能力强;
⚠注意:STM32的IO口高电平为3.3V,即使用STM32的推挽输出模式输出一个高电平信号时,IO引脚电压为3.3V,假设使用IO引脚控制一个5V的继电器,那么电平就不匹配,这时需将该IO 的输出模式改为开漏输出,让输出的高电平电压取决于外部上拉信号。
推挽复用输出与推挽输出区别在于其信号来源于片上外设模块而不是CPU;
从1.9.1 片上外设存储器 节获知,GPIO 寄存器地址从总线基地址偏移而来;
STM32的每组GPIOx 口可控制 16个IO(如PA0~PA15),他们都是通过配置GPIOx 中的 7个寄存器来实现配置的,这7个寄存器被定义在下图的结构体变量中:
每个GPIO 寄存器都可在如下图的位表中查看具体每一位的定义与功能:(这里以GPIOx_BSRR
寄存器为例)
端口配置低寄存器(Port configuration register low,下简称CRL)、端口配置高寄存器(Port configuration register High,下简称CRH)他们只是控制的端口不同,控制原理相同;
由下图可知CRL 共有 32个位(bit),每 4个位控制一个IO 口,也就是它最多只能配置 8个IO 口,另外的 8个由CRH 来控制,这也是需要两个寄存器的原因;
- 端口配置低寄存器(Port configuration register low):控制标号为 0-7的口;
- 端口配置高寄存器(Port configuration register high):控制标号为 8-15的口;
例如:要配置PA0口为最大速度 10MHz的通用推挽输出模式,那就把GPIOA_CRL 的 0~3位配置为
0001
;
IDR(Port input data register)的低 16位对应 16个IO 口,它的功能是读取某个IO 口的电平状态,在输出模式下,也可以读取lO口的电平值;
例如:如当前PA0 的输入电平是低电平
0
,此时读取GPIOA_IDR 的第 0位,得到的值就是0
;
ODR(Port output data register),与IDR 的区别是ODR 是可读可写(rw)寄存器,通过写入该寄存器的某个位,对应的IO 口就会输出对应电平;
例如:把PA0 的输出电平配置为高
1
,配置GPIOA_ODR 的第 0位为1
;
ODR 的另一个重要的功能是当端口被配置为上拉/下拉输入模式时,上拉还是下拉由ODR 配置决定;
例如:已配置GPIOA_CRL 的低 4位为
1000
(即把PA0 配置为上拉/下拉输入模式) ,要使其配置为上拉输入模式,则在GPIOA_ODR中第 0位配置为1
;
BSRR(Port bit set/reset register )用于配置IO 口的输出电平:
1
,则对应IO 口输出为高电平,为0
则不产生效果例如:要配置CPU 输出信号到PA0 的电平为
1
,则对GPIOA_BSRR 的第 0位配置为1
即可;
BRR(Port bit reset register)用于配置某位为0
,起到清除寄存器位的功能;
对于LCKP寄存器,当执行正确的写操作设置了位16时,该寄存器用来锁定端口位的配置,位0-15用于锁定GPIO 端口的配置;
当对相应的端口位执行了LOCK 后,在下次系统复位之前,将不能再更改端口位的配置,每个锁定位控制CRL 和CRH 寄存器中相应4个位;
如没用到串口,
stm32f10x_usart.c
可以删除;
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
//#include "usart.h" // 串口
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED 连接的硬件接口
while(1)
{
LED0=0; // LED 开
delay_ms(300); //延时300ms
LED0=1;
delay_ms(300); //延时300ms
}
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PBout(5) // PB5 输出宏定义
void LED_Init(void); // LED 函数初始化
#endif
led.c
#include "led.h"
// IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
// GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
}
- 读取输入电平函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
读取GPIOx 某个特定引脚的输入电平;uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
- 读取GPIOx 中所有IO 口的输入电平;- 读取输出电平函数:
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 读取GPIOx 某个特定引脚的输出电平;uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
- 读取GPIOx 中所有IO 口的输出电平;- 设置输出电平函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 配置某特定引脚输出为1
;void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 配置某特定引脚输出为0
;void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
https://jingyan.baidu.com/article/4f34706e9fd5d4a387b56da6.html
ws2812.h
:#ifndef __WS2812_H
#define __WS2812_H
#include "sys.h"
#define WS_ARRAY_SIZE 100
#define IN_H PAout(6)=1;
#define IN_L PAout(6)=0;
// 相对精准的延时
// 注:一个空指令__NOP() 的时间约等于 1000/72 ≈ 14 ns
#define Wait10nop {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
#define Wait250ns {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
#define Wait400ns {Wait250ns;Wait10nop;} //388
#define Wait850ns {Wait250ns;Wait10nop;Wait10nop;Wait10nop;Wait10nop;__NOP();__NOP();__NOP();__NOP();__NOP();} //860
//预定义的颜色RGB值
#define WS_DARK 0,0,0
#define WS_WHITE 255,255,255
#define WS_RED 255,0,0
#define WS_GREEN 0,255,0
#define WS_BLUE 0,0,255
#define WS_YELLOW 255,255,0
#define WS_PURPLE 255,0,255
#define WS_CYAN 0,255,255
#define WS_BROWN 165,42,42
//ws2812初始化
void ws2812_init(void);
//设置第ws_num个灯珠的颜色rgb
void ws2812_rgb(u8 ws_num,u8 ws_r,u8 ws_g,u8 ws_b);
//设置前ws_count个灯珠颜色为rgb
void ws2812_rgb_all(u8 ws_count,u8 ws_r,u8 ws_g,u8 ws_b);
//将最新的ws_data[]数组中的值发送至WS2812B模块
void ws2812_refresh(u8 ws_count);
//颜色设置的数据包之间的时间间隔reset code > 50 us
void ws2812_reset(void);
void send_0(void);
void send_1(void);
#endif //__WS2812_H
ws2812.c
:#include "ws2812.h"
#include "delay.h"
u8 ws_data[WS_ARRAY_SIZE]={0}; // 该数组用于记录待传输的RGB数据,每一个灯珠的颜色占用三个字节(24 bit)
/***************************************************************************
** 函数名称 : ws2812_init
** 功能描述 : 单片机GPIO初始化
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210122
** 说 明 : 使用PA6控制WS2812B
***************************************************************************/
void ws2812_init(void) //PA6
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
}
/***************************************************************************
** 函数名称 : ws2812_rgb
** 功能描述 : 设置某一个灯珠的颜色值
** 输入变量 :
ws_num:选择设置第几个LED,以级联顺序而定
ws_r:红色值
ws_g:绿色值
ws_b:蓝色值
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210123
** 说 明 : 每一个灯珠的颜色占用三个字节(24 bit),数据传输顺序按GRB顺序传输,注意高位在前,
***************************************************************************/
void ws2812_rgb(u8 ws_num,u8 ws_r,u8 ws_g,u8 ws_b)
{
ws_data[(ws_num-1)*3]=ws_g;
ws_data[(ws_num-1)*3+1]=ws_r;
ws_data[(ws_num-1)*3+2]=ws_b;
}
/***************************************************************************
** 函数名称 : ws2812_rgb_all
** 功能描述 : 设置前几个灯珠的颜色值
** 输入变量 :
ws_count:选择设置前几个LED,以级联顺序而定
ws_r:红色值
ws_g:绿色值
ws_b:蓝色值
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210123
** 说 明 : 灯珠的颜色占用三个字节(24 bit),数据传输顺序按GRB顺序传输,注意高位在前,
***************************************************************************/
void ws2812_rgb_all(u8 ws_count,u8 ws_r,u8 ws_g,u8 ws_b)
{
static u8 rgb_wsi;
for(rgb_wsi=1;rgb_wsi<=ws_count;rgb_wsi++)
{
ws_data[(rgb_wsi-1)*3]=ws_g;
ws_data[(rgb_wsi-1)*3+1]=ws_r;
ws_data[(rgb_wsi-1)*3+2]=ws_b;
}
}
/***************************************************************************
** 函数名称 : ws2812_refresh
** 功能描述 : 将最新的ws_data[]数组中的值发送至WS2812B模块
** 输入变量 : ws_count:要发送的数据包数,一个数据包为24bit
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210123
** 说 明 : ws_data[] 数组中的每一个字节按位发送,因为高位在前,所以先发送每个字节的高位
***************************************************************************/
void ws2812_refresh(u8 ws_count)
{
u8 ws_ri=0;
for(;ws_ri<ws_count*3;ws_ri++)
{
if((ws_data[ws_ri]&0x80)==0) send_0(); else send_1(); // ws_data[ws_ri]&0x80 的功能是获取最高位的值,以下同理
if((ws_data[ws_ri]&0x40)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x20)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x10)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x08)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x04)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x02)==0) send_0(); else send_1();
if((ws_data[ws_ri]&0x01)==0) send_0(); else send_1();
}
//延时一段时间
ws2812_reset();
}
/***************************************************************************
** 函数名称 : ws2812_reset
** 功能描述 : 颜色设置的数据包之间的时间间隔,reset code > 50 us
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210123
** 说 明 :
***************************************************************************/
void ws2812_reset(void)
{
IN_L; // GPIO输出低电平
delay_us(100);
}
/***************************************************************************
** 函数名称 : send_0
** 功能描述 : 归零码输出低电平0信号
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210122
** 说 明 :
***************************************************************************/
void send_0(void)
{
IN_H;
Wait400ns;
IN_L;
Wait850ns;
}
/***************************************************************************
** 函数名称 : send_1
** 功能描述 : 归零码输出高电平1信号
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210122
** 说 明 :
***************************************************************************/
void send_1(void)
{
IN_H;
Wait850ns;
IN_L;
Wait400ns;
}
main.c
:#include "stm32f10x.h"
#include "string.h"
#include "malloc.h"
#include
#include
#include "delay.h"
#include "usart.h"
#include "common.h"
#include "led.h"
#include "key.h"
#include "ws2812.h"
extern u8 ws_data[];
/***************************************************************************
** 函数名称 : main
** 功能描述 : 工程入口函数
** 输入变量 : 无
** 返 回 值 :
0:程序执行正常
1:程序执行异常
** 最后修改人 : xxx
** 最后更新日期: 20210123
** 说 明 : 无
***************************************************************************/
int main(void)
{
int times = 0;
//初始化
//延时函数初始化
delay_init();
uart_init(115200);
ws2812_init();
printf("System Init OK ...\r\n");
while(1)
{
times++;
if(times > 8)
times = 0;
switch(times)
{
case 0:
ws2812_rgb(1, WS_RED);
ws2812_rgb(2, WS_GREEN);
ws2812_rgb(3, WS_BLUE);
ws2812_rgb(4, WS_WHITE);
ws2812_rgb(5, WS_PURPLE);
ws2812_rgb(6, WS_YELLOW);
ws2812_rgb(7, WS_BROWN);
ws2812_rgb(8, WS_BLUE);
ws2812_refresh(8); // 发送以上8组数据包到WS2812B
break;
case 1:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(1, WS_RED);
ws2812_refresh(8);
break;
case 2:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(2, WS_GREEN);
ws2812_refresh(8);
break;
case 3:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(3, WS_BLUE);
ws2812_refresh(8);
break;
case 4:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(4, WS_WHITE);
ws2812_refresh(8);
break;
case 5:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(5, WS_PURPLE);
ws2812_refresh(8);
break;
case 6:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(6, WS_YELLOW);
ws2812_refresh(8);
break;
case 7:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(7, WS_BROWN);
ws2812_refresh(8);
break;
case 8:
memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8));
ws2812_rgb(8, WS_BLUE);
ws2812_refresh(8);
break;
}
delay_ms(1000);
}
}
环境:MCU GPIO电压3.3V,LED发光电流20mA.
【该理论未实际验证】
1个GPIO控制2个LED的亮灭,共4钟状态。当MCU GPIO资源紧张时可能用到,否则还是建议使用单个GPIO控制一个LED.
在全灭时,如果led会微亮,可以调大限流电阻,或者换用导通压降更大的led
注意事项:
函数索引:
void PCA9555_Init(void);
void PCA9555_WriteOneByte(u8 devAdd,u8 regAdd,u8 data,u8 channel);
void PCA9555_Write(u8 devAdd,u8 regAdd,u8 data0,u8 data1,u8 channel);
u8 PCA9555_Read(u8 devAdd,u8 regAdd,u8 channel);
void OUT_SW(u8 ch,u8 status);
u8 READ_INPUT(u8 ch);
/***************************************************************************
** 函数名称 : PCA9555_Init
** 功能描述 : PCA9555芯片初始化
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
void PCA9555_Init(void)
{
// 芯片2设备地址:0x44
PCA9555_WriteOneByte(0X44,0X06,0x00,1);delay_ms(20); //配置设备地址为0x44的GPIO 0为全0
PCA9555_WriteOneByte(0X44,0X07,0x00,1);delay_ms(20); //配置设备地址为0x44的GPIO 1为全0
PCA9555_WriteOneByte(0X44,0X02,0XFF,1);delay_ms(20); //配置设备地址为0x44的输出口0为全1
PCA9555_WriteOneByte(0X44,0X03,0xFF,1);delay_ms(20); //配置设备地址为0x44的输出口1为全1
// 芯片2设备地址:0x40
PCA9555_WriteOneByte(0X40,0X06,0xFF,1);delay_ms(20); //配置设备地址为0x40的GPIO 0为全1
PCA9555_WriteOneByte(0X40,0X07,0xFF,1);delay_ms(20); //配置设备地址为0x40的GPIO 1为全1
}
/***************************************************************************
** 函数名称 : PCA9555_WriteOneByte
** 功能描述 : 向PCA9555写一个字节
** 输入变量 :
1. devAdd:芯片设备地址
2. regAdd:寄存器地址
3. data:字节数据
4. channel:IIC通道
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
void PCA9555_WriteOneByte(u8 devAdd,u8 regAdd,u8 data,u8 channel)
{
u8 flag = 0;
u8 save_time = 0;
IIC_Device.IIC_Channel = channel;
if(IIC_Device.IIC_Channel==6)IIC_Device.Bottom_pca9555_flag = 0;
do
{
IIC_Start();
IIC_WRITE_BYTE(devAdd); //写从属地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(regAdd); //写寄存器地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(data); //写数据
if(IIC_Recelve_Ack()==0)
{
flag = 0;
}
else flag = 1;
}
else flag = 1;
}
else {flag = 1;}
save_time++;
}
while(flag==1&&save_time<200);
if((flag==1||save_time>=200)&&IIC_Device.IIC_Channel==6)IIC_Device.Bottom_pca9555_flag = 1;
IIC_Stop();
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
}
/***************************************************************************
** 函数名称 : PCA9555_Write
** 功能描述 : 向PCA9555写两个字节
** 输入变量 :
1. devAdd:芯片设备地址
2. regAdd:寄存器地址
3. data0:字节数据0
4. data1:字节数据1
5. channel:IIC通道
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
void PCA9555_Write(u8 devAdd,u8 regAdd,u8 data0,u8 data1,u8 channel)
{
u8 flag = 0;
u8 save_time = 0;
IIC_Device.IIC_Channel = channel;
if(IIC_Device.IIC_Channel==6)IIC_Device.Bottom_pca9555_flag = 0;
do
{
IIC_Start();
IIC_WRITE_BYTE(devAdd); //写从属地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(regAdd); //写寄存器地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(data0); //写数据
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(data1); //写数据
if(IIC_Recelve_Ack()==0)
{
flag = 0;
}
else flag = 0;
}
else flag = 1;
}
else flag = 1;
}
else {flag = 1;}
save_time++;
}
while(flag==1&&save_time<200);
if((flag==1||save_time>=200)&&IIC_Device.IIC_Channel==6)IIC_Device.Bottom_pca9555_flag = 1;
IIC_Stop();
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
}
/***************************************************************************
** 函数名称 : PCA9555_Read
** 功能描述 : 向PCA9555读一个字节
** 输入变量 :
1. devAdd:芯片设备地址
2. regAdd:寄存器地址
3. channel:IIC通道
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
u8 PCA9555_Read(u8 devAdd,u8 regAdd,u8 channel)
{
u8 flag = 0;
u8 read_time = 0;
u8 PCA9555_data1_temp=0;
IIC_Device.IIC_Channel = channel;
// if(IIC_Device.IIC_Channel==2)IIC_Device.Bottom_pca9555_flag = 0;
// if(IIC_Device.IIC_Channel==3)IIC_Device.acc_pca9555_flag = 0;
do
{
IIC_Start();
IIC_WRITE_BYTE(devAdd); //写从属地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(regAdd); //写寄存器地址
if(IIC_Recelve_Ack()==0)
{
IIC_Start();
IIC_WRITE_BYTE(devAdd|0x01); //从PCA9555读数据
if(IIC_Recelve_Ack()==0)
{
PCA9555_data1_temp = IIC_Read_Byte(1); //最后一个字节MCU不应答
flag = 0;
}
else {flag = 1;}
}
else {flag = 1;}
}
else {flag = 1;}
read_time++;
}
while(flag==1&&read_time<200);
// if(flag==1&&IIC_Device.IIC_Channel==2)IIC_Device.Bottom_pca9555_flag = 1;
// if(flag==1&&IIC_Device.IIC_Channel==3)IIC_Device.acc_pca9555_flag = 1;
IIC_Stop();
delay_Nus(250);
return PCA9555_data1_temp;
}
/***************************************************************************
** 函数名称 : OUT_SW
** 功能描述 : PCA9555输出控制
** 输入变量 :
1. ch:端口
2. status:开关,ON:1,OFF:0
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
void OUT_SW(u8 ch,u8 status)
{
if(ch==1) // 0.0
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xfe; // 读取除0.0之外的所有位状态并保存,将0.0对应的端口置0
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1); // 写入数据,将0.0置于高电平
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X01; // 读取除0.0之外的所有位状态并保存,将0.0对应的端口置1
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);// 写入数据,将0.0置于低电平
}
delay_ms(30);
}
else if(ch==2) // 0.1
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xfd;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X02;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==3) // 0.2
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xfb;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X04;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==4)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xf7;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X08;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==5)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xEF;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X10;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==6)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xDF;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X20;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==7)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0xBF;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X40;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==8)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)&0x7F;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x02,1)|0X80;
PCA9555_WriteOneByte(0X44,0X02,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==9)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xFE;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X01;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==10)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xFD;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X02;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==11)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xFB;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X04;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==12)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xF7;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X08;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==13)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xEF;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X10;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==14)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xDF;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X20;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==15)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0xBF;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X40;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
else if(ch==16)
{
if(status==ON)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)&0x7F;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
else if(status==OFF)
{
IIC_Device.Bottom_pca9555_output0 = PCA9555_Read(0x44,0x03,1)|0X80;
PCA9555_WriteOneByte(0X44,0X03,IIC_Device.Bottom_pca9555_output0,1);
}
delay_ms(30);
}
}
/***************************************************************************
** 函数名称 : READ_INPUT
** 功能描述 : PCA9555输入控制
** 输入变量 :
1. ch:端口
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210330
** 说 明 : 无
***************************************************************************/
u8 READ_INPUT(u8 ch)
{
u8 data_temp;
if(ch==1) // 0.0
{
data_temp = PCA9555_Read(0x40,0x00,1); //选择设备地址为0x40的芯片,选择端口0,读取端口0上的所有IO状态
data_temp &= 0x01; //清洗数据,只保留0.0的IO状态
if(data_temp==0x01)return 1; //若0.0为高电平,返回1
else return 0;//若0.0为低电平,返回0
}
else if(ch==2) // 0.1
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x02;
if(data_temp==0x02)return 1;
else return 0;
}
else if(ch==3)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x04;
if(data_temp==0x04)return 1;
else return 0;
}
else if(ch==4)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x08;
if(data_temp==0x08)return 1;
else return 0;
}
else if(ch==5)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x10;
if(data_temp==0x10)return 1;
else return 0;
}
else if(ch==6)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x20;
if(data_temp==0x20)return 1;
else return 0;
}
else if(ch==7)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x40;
if(data_temp==0x40)return 1;
else return 0;
}
else if(ch==8)
{
data_temp = PCA9555_Read(0x40,0x00,1);
data_temp &= 0x80;
if(data_temp==0x80)return 1;
else return 0;
}
else if(ch==9)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x01;
if(data_temp==0x01)return 1;
else return 0;
}
else if(ch==10)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x02;
if(data_temp==0x02)return 1;
else return 0;
}
else if(ch==11)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x04;
if(data_temp==0x04)return 1;
else return 0;
}
else if(ch==12)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x08;
if(data_temp==0x08)return 1;
else return 0;
}
else if(ch==13)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x10;
if(data_temp==0x10)return 1;
else return 0;
}
else if(ch==14)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x20;
if(data_temp==0x20)return 1;
else return 0;
}
else if(ch==15)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x40;
if(data_temp==0x40)return 1;
else return 0;
}
else if(ch==16)
{
data_temp = PCA9555_Read(0x40,0x01,1);
data_temp &= 0x80;
if(data_temp==0x80)return 1;
else return 0;
}
else
{
return 0;
}
}
单片机、FPGA、ARM、DSP各自的特点及应用