文章目录

  • 1. 综述
    • 1.1 集成开发环境
      • 1.1.1 常用开发工具简介
    • 1.2 常用资料手册简介
      • 1.2.1 芯片手册
      • 1.2.2 参考手册
      • 1.2.3 勘误手册
      • 1.2.4 Cotex-M3/M4权威指南
      • 1.2.5 应用手册
      • 1.2.6 在ST官网上查找相应手册
      • 1.2.7 在ST官网上下载对应芯片软件包
      • 1.2.8 正点原子资料下载中心
    • 1.3 HAL库介绍
    • 1.4 CMSIS软件包介绍
    • 1.5 ARM 架构(含 Cortex-M 系列)数据类型
    • 1.6 堆栈
    • 1.7 bootloader
  • 2. GPIO
    • 2.1 4种输入模式
      • 2.1.1 输入浮空
      • 2.1.2 输入上拉
      • 2.1.3 输入下拉
      • 2.1.4 模拟输入
    • 2.2 4种输出模式
      • 2.2.1 开漏输出
      • 2.2.2 开漏复用输出
      • 2.2.3 推挽输出
      • 2.2.4 推挽复用输出
    • 2.3 GPIO 寄存器
      • 2.3.1 端口配置寄存器(CRL/CRH)
      • 2.3.2 端口输入寄存器(IDR)
      • 2.3.3 端口输出数据寄存器(ODR)
      • 2.3.4 端口位配置/清除寄存器(BSRR)
      • 2.3.5 端口位清除寄存器(BRR)
    • 2.4 端口复用(AFIO)
      • 2.4.1 端口复用配置
    • 2.5 端口重映射(Remap)
      • 2.5.1 端口重映射配置
      • 2.5.2 部分重映射和完全重映射
    • 2.6 GPIO应用
      • 2.6.1 点亮一个灯
      • 2.6.2 STM32F103芯片的PB3 PB4 PA15引脚如何用作普通端口
      • 2.6.3 集成RGB LED器件 - WS2812B控制
      • *2.6.4 1个GPIO控制2个LED的亮灭
    • 2.7 IO扩展(CAT9555和PCA9555)
  • 参考&收藏

1. 综述


1.1 集成开发环境

  • STM32的集成开发环境(IDE)有两种:MDKIAR,其中IAR向下兼容性差,建议使用IAR8.3版本;
  • STM32的调试器可选:JLINK、CMSIS-DAP、ULINK、STLINK;

1.1.1 常用开发工具简介

1.2 常用资料手册简介


1.2.1 芯片手册

Data sheet;
芯片的所有数据可从官方数据手册中查看,如IC的Pin 定义、电器特性、机械特性、料号定义等,如下在芯片STM32F103x8B 的数据手册中查看其型号为LQFP100的 100个引脚的物理分布和定义:

1.2.2 参考手册

Reference Manual;
对芯片的外设的具体描述和功能介绍;

1.2.3 勘误手册

Errata Sheet;
描述IC某些功能的局限性(硬件bug)并给出解决方法;

1.2.4 Cotex-M3/M4权威指南

官方的STM32内核手册;

1.2.5 应用手册

Application Note;
官方的针对不同应用场合的描述性文档,一般配套有固件例程;

1.2.6 在ST官网上查找相应手册

  1. 进入ST官网https://www.st.com/content/st_com/en.html;
  2. 搜索栏输入对应芯片,如STM32F103
  3. 下载目标文档;

1.2.7 在ST官网上下载对应芯片软件包

  1. 进入ST官网https://www.st.com/content/st_com/en.html;
  2. 搜索栏输入对应芯片软件包,如STM32CubeH7
  3. 下载目标软件包;

软件包组成框图:

1.2.8 正点原子资料下载中心

http://www.openedv.com/docs/index.html#

1.3 HAL库介绍

在一些比较新的或高性能的芯片中会带有HAL库

HAL(Hardware Abstraction Layer - 硬件抽象层)库包含在芯片的软件包内,是芯片的外设驱动包,代码文件路径为(以STM32H7为例):Drivers\STM32H7xx_HAL_Driver;每个源文件开头都带有使用说明;

部分代码文件截图:

1.4 CMSIS软件包介绍

CMSIS(Cortex Microcontroller Software Interface Standard - 微控制器软件接口标准)是ARM官方设计的驱动包,其目的是统一各大芯片厂商的外设驱动、DSP数字信号处理、下载器和各个主流的RTOS;

CMSIS软件包的获取途径:

  1. 芯片软件包中;
  2. MDK安装目录,ARM/PACK/ARM/CMSIS/版本号/CMSIS
  3. GitHub,https://github.com/ARM-software/CMSIS_5;
  4. RAM官网;

CMSIS的组成框图:

简单介绍CMSIS软件包中的几个常用的文件夹:

  • Core:Cortex-M处理器内核和外设的API,为处理器内核提供标准化接口;
  • DAP:ARM官方推出的下载器固件;
  • Documentation:CMSIS软件包的Help文档;
  • Driver:ARM的驱动框架,该驱动包与HAL库的区别是该驱动包本身也会调用到HAL库的一些API,但同时它也封装了一些更好用的API,该驱动包路径(前提是安装了芯片的软件包):ARM\PACK\Keil\STM32H7xx_DFP\2.1.0\CMSIS\Driver,其支持外设如下图:
  • DSP_Lib:ARM提供的DSP库,含源码;
  • Include:封装了很多内核方面的API,是工程中务必包含的重要头文件路径;
  • Lib:GCC和MDK格式的DSP库文件;
  • NN:ARM推出的神经网络库,框图如下:
  • RTOS:RTX4和CMSIS-RTOS V1封装层,含代码;
  • RTOS2:RTX5和CMSIS-RTOS V2封装层,含代码;
  • SVD:System View Description - 系统视图描述;对芯片的外设、存储器等进行详细描述,编译器需要用到该文件,不同系列芯片有不同的SVD,在MDK的Option - Target中可看到被调用的svd后缀文件;
  • Utilities:一些实用的小文件;

1.5 ARM 架构(含 Cortex-M 系列)数据类型

  • 这些定义在头文件 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)... 

1.6 堆栈

  • 栈(stack)空间:用于局部变量、函数调时现场保护和返回地址、函数的形参等。
  • 堆(heap)空间:主要用于动态内存分配,即使用 malloccallocrealloc 等函数分配的变量空间是在堆上。以 STM32H7 为例,堆栈是在 startup_stm32h743xx.s 文件里面设置:

1.7 bootloader

STM32 的系统存储区自带 bootloader程序,此程序是 ST 在芯片出厂时烧录进去的,主要用于将用户应用程序下载到芯片内部 Flash。支持 USB,SPI,I2C,CAN,UART 等接口方式下载。

  • 单片机烧录的方式

    1. ISP:In System Programing - 在系统编程。⽐如使⽤STC-ISP对STC芯⽚编程,还有利用Flash loader对STM32编程等,切换BOOT0、BOOT1让芯片进boot程序。⽀持ISP的芯⽚⼀般在芯⽚内部固化了⼀段(⽤ISP升级的)boot程序。
    2. ICP:包括In Circuit Programing - 在电路编程和ICSP(In-Circuit Serial Programming)- 在电路串⾏编程。如:对EEPROM编程等。一般来说利⽤J-Link、ST-Link、e-Link32等⼯具进⾏编程也属于在电路编程(ICP)。
    3. IAP:In applicating Programing - 在应⽤编程:即Bootloader使用的单片机编程方式,可以简单理解为:在程序运⾏的过程中进⾏编程(升级程序,更新固件)。 IAP是⽤户⾃⼰的程序在运⾏过程中对User Flash的部分区域进⾏烧写,⽬的是为了在产品发布后可以⽅便地通过预留的通信⼝对产品中的固件程序进⾏更新升级。
  • Bootloader程序升级流程

    1. 上电后检查是否需要对第二部分代码进行更新
    2. 如果不需要就转到步骤4
    3. 执行更新程序
    4. 设置系统调度,跳转到用户应用程序运行

2. GPIO

GPIO(General-purpose input/output)通用输入输出端口;
STM32 的所有IO 口都可作为中断输入;


  1. 电压与电平:

    • 电压:指模拟信号,单位用V表示;
    • 电平:数字信号,以01 表示;
  2. VDD,VCC,VSS,GND,地之间有何区别?

2.1 4种输入模式

2.1.1 输入浮空

输入浮空下,模拟信号从IO 口进入,上下拉电阻开关都断开(浮空状态),通过TTL 施密特触发器把模拟信号转换为数字信号(AD转换),再到输入数据寄存器,CPU 再读取寄存器;

注:在MCU 程序内对应GPIO 引脚配置为输入浮空模式下,当外部无输入信号且无外部上/下拉电阻时,MCU GPIO 此时呈现高阻态

2.1.2 输入上拉

输入上拉与浮空输入的区别在于,输入上拉模式下上拉电阻(约30k~50k ohm)开关接通,外部信号电平通过上拉电阻被拉高到3.3V;

2.1.3 输入下拉

输入下拉与浮空输入的区别在于,输入下拉模式下下拉电阻(约30k~50k ohm)开关接通,外部信号电平通过下拉电阻被拉低到GND;

2.1.4 模拟输入

模拟输入模式下,模拟信号从IO 口进入,上下拉电阻开关都断开,TTL 施密特触发器截止,模拟信号直接输入到CPU;

2.2 4种输出模式

2.2.1 开漏输出

CPU 向位寄存器输出数字信号,到输出数据寄存器,到输出控制电路,到N-MOS 管,到IO 口;

此模式下P-MOS 管不工作;

  • Case1:CPU输出1时,N-MOS 管截止,输出的实际信号由IO 口的上下拉电阻决定,其实际输出的电平信号同时也可以通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;
  • Case2:CPU输出0时,N-MOS 管导通,输出电平信号被N-MOS 管拉到Vss(公共地),IO 口输出低电平0,同理该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;

@_@

2.2.2 开漏复用输出

开漏复用输出与开漏输出的区别在于其信号来源于片上外设模块而不是CPU;

2.2.3 推挽输出

推挽输出与开漏输出的区别在于推挽输出模式下输出驱动器的P-MOS 管正常工作;

  • Case1:CPU输出1时,P-MOS 管导通,输出电平信号被P-MOS 管拉到Vdd(器件工作电压),IO 口输出高电平1,该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;

  • Case2:与推挽输出模式同理,CPU输出0时,N-MOS 管导通,输出电平信号被N-MOS 管拉到Vss(公共地),IO 口输出低电平0,同理该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;

注意:STM32的IO口高电平为3.3V,即使用STM32的推挽输出模式输出一个高电平信号时,IO引脚电压为3.3V,假设使用IO引脚控制一个5V的继电器,那么电平就不匹配,这时需将该IO的输出模式改为开漏输出,让输出的高电平电压取决于外部上拉信号。

2.2.4 推挽复用输出

推挽复用输出与推挽输出区别在于其信号来源于片上外设模块而不是CPU;

2.3 GPIO 寄存器

STM32的每组GPIO 口可控制 16个IO(如PA0~PA15),他们都是通过配置GPIO 中的 7个寄存器来实现配置的;

STM32中每个IO 都可自有编程,但IO 寄存器必须按 32位字被访问(不允许半字或字节访问)

2.3.1 端口配置寄存器(CRL/CRH)

端口配置低寄存器(Port configuration register low,下简称CRL)、端口配置高寄存器(Port configuration register High,下简称CRH)他们只是控制的端口不同,控制原理相同;

由下图可知CRL 共有 32个位(bit),每 4个位控制一个IO 口,也就是它最多只能配置 8个IO口(32/4=8),另外的 8个由CRH 来控制,这也是需要两个寄存器的原因;

Eg:要配置PA0口为最大速度 10MHz的通用推挽输出模式,那就把GPIOA_CRL 的 0~3位配置为0001

端口配置低寄存器(Port configuration register low):控制标号为 0-7的口;
端口配置高寄存器(Port configuration register high):控制标号为 8-15的口;

2.3.2 端口输入寄存器(IDR)

IDR(Port input data register)的低 16位对应 16个IO 口,它的功能是读取某个IO 口的电平状态;

Eg:如当前PA0 的输入电平是低电平0,此时读取GPIOA_IDR 的第 0位,得到的值就是0

2.3.3 端口输出数据寄存器(ODR)

  1. ODR(Port output data register),与IDR 的区别是ODR 是可读可写(rw)寄存器,通过写入该寄存器的某个位,对应的IO 口就会输出对应电平;

Eg:把PA0 的输出电平配置为高1,配置GPIOA_ODR 的第 0位为1

  1. ODR 的另一个重要的功能是当端口被配置为上拉/下拉输入模式时,上拉还是下拉由ODR 配置决定;

Eg:已配置GPIOA_CRL 的低 4位为1000(即把PA0 配置为上拉/下拉输入模式) ,要使其配置为上拉输入模式,则在GPIOA_ODR中第 0位配置为1

2.3.4 端口位配置/清除寄存器(BSRR)

BSRR(Port bit set/reset register )用于配置IO 口的输出电平,低 16位对应位配置为1,则对应IO 口输出为高电平,为0则不产生效果,高 16位作用相反
它与ODR 的区别在于:见GPIO 输出模式电路图可知,输出信号来自于CPU 时,BSRR间接控制 ODR,从而决定IO 输出电平,而当输出信号来自于片上外设时,输出电平直接由ODR 决定;

Eg:要配置CPU 输出信号到PA0 的电平为1,则对GPIOA_BSRR 的第 0位配置为1即可;

2.3.5 端口位清除寄存器(BRR)

BRR(Port bit reset register)用于配置某位为0,起到清除寄存器位的功能,与BSRR 的低 16位功能相同;

2.4 端口复用(AFIO)

AFIO(Alternate function I/O ) 即一些端口不仅可作GPIO使用,还可复用为一些内置外设的功能引脚,这一过程就叫端口复用。具体哪些IO 可复用为哪些功能,可查阅对应芯片的datasheet;

如下图是STM32某一个芯片的PA9、PA10引脚复用说明,以PA9为例,该引脚上电默认功能为GPIO,也可复用为USART1_TX(串口1的数据发送引脚)或TIM1_CH2(定时器1的2号通道)。

2.4.1 端口复用配置

以下代码介绍了将GPIO PA9、PA10复用为内置外设USART1_TX、USART1_RX引脚的步骤(串口1工作在全双工模式):

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);

2.5 端口重映射(Remap)

重映射是为了方便PCB 布线,如串口 1默认的引脚为PA9、PA10,通过重映射,可把串口 1配置到引脚PB6、PB7.

如下图是STM32某一个芯片的端口重映射表,GPIO引脚PB6、PB7的默认内置外设功能为 Alternate function 中的Default,而重映射使能后,其引脚的内置外设功能即可变为串口1的数据收发引脚。

从另一个角度看,如下图是STM32某一个芯片的内置外设串口1的功能引脚重映射表,其默认引脚为PA9、PA10,在对应的重映射使能后,其引脚映射到了PB6、PB7.

2.5.1 端口重映射配置

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重映射功能

2.5.2 部分重映射和完全重映射

关于重映射的功能,除了查看芯片参考手册外的重映射表外,还可以从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完全重映射

2.6 GPIO应用

2.6.1 点亮一个灯

  • 硬件连接:如下,在原理图中使用PB5 引脚来作为LED0 的IO 口,硬件连接中LED 通过一个 510ohm 的电阻拉到 VCC3.3V;
  • 控制原理:PB5 输出低电平,LED被导通点亮,GPIOB 输出模式选择最常用的推挽输出模式;
  • 软件
    1. 需要用到的官方库文件如下:

    如没用到串口,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);   //延时300msLED0=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口速度为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure);                     //根据设定参数初始化GPIOB.5
// GPIO_SetBits(GPIOB,GPIO_Pin_5);                       //PB.5 输出高
}
  • 读取输入电平函数

    1. uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 读取GPIOx 某个特定引脚的输入电平;
    2. uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx) - 读取GPIOx 中所有IO 口的输入电平;
  • 读取输出电平函数
    1. uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) - 读取GPIOx 某个特定引脚的输出电平;
    2. uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx) - 读取GPIOx 中所有IO 口的输出电平;
  • 设置输出电平函数
    1. void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) - 配置某特定引脚输出为 1
    2. void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) - 配置某特定引脚输出为 0
    3. void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
    4. void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)

2.6.2 STM32F103芯片的PB3 PB4 PA15引脚如何用作普通端口

https://jingyan.baidu.com/article/4f34706e9fd5d4a387b56da6.html

2.6.3 集成RGB LED器件 - WS2812B控制

  • 头文件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 <stdio.h>
#include <stdlib.h>#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组数据包到WS2812Bbreak;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);        }
}

*2.6.4 1个GPIO控制2个LED的亮灭

环境:MCU GPIO电压3.3V,LED发光电流20mA.
【该理论未实际验证】

1个GPIO控制2个LED的亮灭,共4钟状态。当MCU GPIO资源紧张时可能用到,否则还是建议使用单个GPIO控制一个LED.

  • 红灯亮,绿灯灭:GPIO输出高电平

  • 红灯灭,绿灯亮:GPIO输出低电平

  • 都灭:GPIO设为高阻态,由于两个LED灯总的导通压降要求大于3.3V,所以两个LED灯都不导通

在全灭时,如果led会微亮,可以调大限流电阻,或者换用导通压降更大的led

  • 都亮:GPIO交替输出高低电平,只要高低电平切换的频率够高,由于视觉暂留效应,人眼看到的就是两个灯都常亮

注意事项:

  1. 注意MCU GPIO的电流驱动能力。GPIO能承受的电流要大于LED灯流过的电流。以下截图出自STM32芯片的数据手册,它的GPIO可以驱动或吸入8mA的电流。
  2. 两个LED漏电流都要很低,否则会出现全灭状态下LED微亮的情况。

2.7 IO扩展(CAT9555和PCA9555)

函数索引:
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设备地址:0x44PCA9555_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为全1PCA9555_WriteOneByte(0X44,0X03,0xFF,1);delay_ms(20);   //配置设备地址为0x44的输出口1为全1 // 芯片2设备地址:0x40PCA9555_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:字节数据04. data1:字节数据15. 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对应的端口置0PCA9555_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对应的端口置1PCA9555_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为高电平,返回1else 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各自的特点及应用

STM32理论 —— 综述、GPIO相关推荐

  1. 量子计算机计算时间复杂度,量子计算复杂性理论综述.PDF

    量子计算复杂性理论综述 第卷第期 计 算 机 学 报 39 12 Vol.39No.12 年月 201612 CHINESEJOURNALOFCOMPUTERS Dec.2016 量子计算复杂性理论综 ...

  2. STM32基础入门——GPIO详解

    目录 一.GPIO的简介 二.GPIO工作模式-----输入 1.浮空输入模式 2.上拉输入模式 3.下拉输入模式 4.模拟输入模式 三.GPIO工作模式-----输出 1.开漏输出模式 2.推挽输出 ...

  3. STM32单片机的GPIO寄存器CRL、CRH、IDR、ODR、BRSS、BRR

    STM32的寄存器多如牛毛,要一个一个的弄清楚谈何容易呀,所以我们在学习的时候要抓住重点,挑重要的进行学习和记忆,因为STM32一个一个去配置寄存器的话相当的麻烦,我们实际开发的话也不会一个一个去配置 ...

  4. 【STM32】Cortex_M4 GPIO口概述知识总结

    一.GPIO概述 1.什么是GPIO口? GPIO口:简单而言,就是芯片用来控制或者采集外部器件的相关信息,这个又可以称之为输入输出的端口. 2.GPIO有什么作用? 是芯片用来感知外部世界并且用来控 ...

  5. STM32【H7】理论——综述、HAL库简述

    文章目录 1. STM32H7芯片简介 1.1 STM32H7与STM32F1.F4系列芯片的区别 1.2 硬件框图 1.3 STM32H7各型号对比 1.4 总线框图和时钟 1.5 AXI总线 1. ...

  6. STM32外设之GPIO的推挽输出和开漏输出模式详解

    文章目录 1 GPIO结构框图 2 推挽输出模式 3 开漏输出模式 1 GPIO结构框图 先来看一下GPIO输出的部分: 2 推挽输出模式 所谓的推挽输出模式,是根据这两个 MOS管的工作方式来命名的 ...

  7. STM32之的GPIO推挽输出与开漏输出的区别

    首先看以下STM32的GPIO的原理图如下: 当端口配置为输出时: 开漏模式:输出0 时,N-MOS 导通,P-MOS 不被激活,输出0. 输出1 时,N-MOS 高阻, P-MOS 不被激活,输出1 ...

  8. 意识理论综述:众多竞争的意识理论如何相互关联?

    来源: 集智俱乐部 作者:Anil K. Seth & Tim Bayne 译者:李路凯.陈斯信 编辑:邓一雪  导语 意识和底层的神经活动之间如何联系起来?试图解释这一问题的意识理论层出不穷 ...

  9. STM32理论 —— DAC、DMA

    文章目录 1. DAC 1.1 内置DAC 1.2 MCP4725 1.3 可编程信号发生器 - AD9833 1.3.1 控制寄存器 1.3.2 频率和相位寄存器 1.3.3 输出引脚 1.3.4 ...

  10. STM32理论 ——通信

    文章目录 1. 数据通信介绍 1.1 并行/串行通信 2. USART串口通信(STM32H7系列) 2.1 串口的硬件框图 2.2 串口的基本功能特性 2.3 串口的自适应波特率 2.4 串口的数据 ...

最新文章

  1. 时间和邮箱的正则表达式,获取当前时间函数
  2. 360金融发布Q2财报:净利6.92亿,同比增长114%,大数据与AI加持的科技服务是新亮点?
  3. Python 引用全局变量提示:local variable referenced before assignment. 问题解决办法,global使用方法介绍
  4. 亿级流量电商详情页系统实战:缓存架构+高可用服务架构+微服务架构
  5. MySQL复习资料(二)——MySQL-DDL语句
  6. C++(静态)(常量)数据进行初始化问题以及静态变量析构
  7. 前端学习(3161):react-hello-react之样式的模块化
  8. FunDA(7)- Reactive Streams to fs2 Pull Streams
  9. SQL Server中的TempDB管理——TempDB基本知识(为什么需要版本存储区)
  10. python 个人项目_80个Python练手项目列表,学完立马成大神 牛逼了
  11. 全局变量只能初始化不能赋值
  12. 【Luyten反编译工具】
  13. 【Unity3D 灵巧小知识点】 ☀️ | 层级面板中的 ‘小手指‘ 作用: 在Scen中将该物体设置为不可选中状态
  14. iOS 手动实现KVO / iOS KVO底层原理
  15. 威纶通触摸屏与温控器进行MODBUS通信并通过宏指令将数据发送给PLC的具体方法
  16. 号码认证一键免密登录,让验证更简单!
  17. 无障碍感受 北京之美
  18. 梯度下降法-学习率选取
  19. HDU 6595 Everything Is Generated In Equal Probability (期望dp,线性推导)
  20. Linux无线网络架构

热门文章

  1. Python 编程之Tkinter的使用01
  2. Linux MySQL8.0.11版本升级到MySQL8.0.23【数据库备份】
  3. easyui教程 php,Easyui 创建子网格_EasyUI 教程
  4. 华为od机考真题-数大雁,1419,数青蛙
  5. 微型计算机硬盘接口种类,各种类型的固态硬盘接口形态分类
  6. 阿里云新优惠活动,幸运券免费领取
  7. 总结VS下opencv无法正常显示图片的几种问题及其解决方案
  8. Android使用网络打印机打印
  9. 概率论知识回顾(十):二维连续随机变量分布函数和联合密度函数
  10. ERROR in ./node_modules/element-plus/es/components/menu-item-group/style/css2.mjs 2:0-54