文章目录
- 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.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 PA如何用15引脚作为普通端口?
- 2.6.3 集成RGB LED器件 - WS2812B控制
- *2.6.4 1个GPIO控制2个LED的亮灭
- 2.7 IO扩展(CAT9555和PCA9555)
- 参考&收藏
1. 综述
1.1 综合开发环境
- STM综合开发环境(IDE)有两种:、,其中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在官网上找到相应的手册
- 进入ST官网https://www.st.com/content/st_com/en.html;
- 搜索栏输入相应的芯片,如STM32F103;
- 下载目标文件;
1.2.7 在ST官网下载相应的芯片软件包
- 进入ST官网https://www.st.com/content/st_com/en.html;
- 搜索栏输入相应的芯片软件包,如STM32CubeH7;
- 下载目标软件包;
软件包组成框图:
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获取软件包的方式:
- 芯片软件包;
- MDK安装目录,ARM/PACK/ARM/CMSIS/版本号/CMSIS;
- GitHub,https://github.com/ARM-software/CMSIS_5;
- 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库,含源码;
- 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 系列)数据类型
- :
/* 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;
- :
/* 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 堆栈
- :用于局部变量、函数调时现场保护和返回地址、函数的形参等。
- :主要用于动态内存分配,即使用
malloc
、calloc
、realloc
等函数分配的变量空间是在堆上。以 STM32H7 为例,堆栈是在startup_stm32h743xx.s
文件里面设置:
1.7 bootloader
STM32 的系统存储区自带 bootloader程序,此程序是 ST 在芯片出厂时烧录进去的,主要用于将用户应用程序下载到芯片内部 Flash。支持 USB,SPI,I2C,CAN,UART 等接口方式下载。
-
:
- :In System Programing - 在系统编程。⽐如使⽤STC-ISP对STC芯⽚编程,还有利用Flash loader对STM32编程等,切换BOOT0、BOOT1让芯片进boot程序。⽀持ISP的芯⽚⼀般在芯⽚内部固化了⼀段(⽤ISP升级的)boot程序。
- :包括In Circuit Programing - 在电路编程和(In-Circuit Serial Programming)- 在电路串⾏编程。如:对EEPROM编程等。一般来说利⽤J-Link、ST-Link、e-Link32等⼯具进⾏编程也属于在电路编程(ICP)。
- :In applicating Programing - 在应⽤编程:即Bootloader使用的单片机编程方式,可以简单理解为:在程序运⾏的过程中进⾏编程(升级程序,更新固件)。 IAP是⽤户⾃⼰的程序在运⾏过程中对User Flash的部分区域进⾏烧写,⽬的是为了在产品发布后可以⽅便地通过预留的通信⼝对产品中的固件程序进⾏更新升级。
-
:
- 上电后检查是否需要对第二部分代码进行更新
- 如果不需要就转到步骤4
- 执行更新程序
- 设置系统调度,跳转到用户应用程序运行
2. GPIO
GPIO(General-purpose input/output)通用输入输出端口; STM32 的所有IO 口都可作为中断输入;
电压与电平:
- 电压:指模拟信号,单位用
V
表示;- 电平:数字信号,以
0
或1
表示;VDD,VCC,VSS,GND,地之间有何区别?
2.1 4种输入模式
2.1.1 输入浮空
输入浮空下,模拟信号从IO 口进入,上下拉电阻开关都断开(浮空状态),通过TTL 施密特触发器把模拟信号转换为数字信号(AD转换),再到输入数据寄存器,CPU 再读取寄存器;
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 管不工作;
- :CPU输出
1
时,N-MOS 管截止,输出的实际信号由IO 口的上下拉电阻决定,其实际输出的电平信号同时也可以通过输入浮空模式读回到输入数据寄存器,再由CPU 读取; - :CPU输出
0
时,N-MOS 管导通,输出电平信号被N-MOS 管拉到Vss(公共地),IO 口输出低电平0
,同理该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取;
@_@
2.2.2 开漏复用输出
开漏复用输出与开漏输出的区别在于其信号来源于片上外设模块而不是CPU;
2.2.3 推挽输出
推挽输出与开漏输出的区别在于推挽输出模式下输出驱动器的P-MOS 管正常工作;
-
:CPU输出
1
时,P-MOS 管导通,输出电平信号被P-MOS 管拉到Vdd(器件工作电压),IO 口输出高电平1
,该输出电平信号也可通过输入浮空模式读回到输入数据寄存器,再由CPU 读取; -
:与推挽输出模式同理,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 都可自有编程,但;
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口为,那就把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)
- ODR(Port output data register),与IDR 的区别是,通过写入该寄存器的某个位,对应的IO 口就会输出对应电平;
Eg:把PA0 的输出电平配置为高
1
,配置GPIOA_ODR 的第 0位为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
则不产生效果,; 它与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)
即一些端口不仅可作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 输出模式选择最常用的推挽输出模式;
- :
- 需要用到的官方库文件如下:
如没用到串口,
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)
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(