【个人总结】4. 毫米波雷达开发 工作三年的嵌入式常见知识点梳理及开发技术要点(欢迎指正、补充)

【个人总结】4. 毫米波雷达开发 工作三年的嵌入式常见知识点梳理及开发技术要点(欢迎指正、补充)

工作快三年以来 分别进行了嵌入式MCU及外设开发、RTOS、传感器、文件系统及USB、Linux、GUI、通讯协议、毫米波雷达、少量的DSP和物联网开发。
特此总结:
1. 开发基础
2. MCU开发
3. 外设开发
4. 毫米波雷达开发
5. 传感器、外部芯片开发
6. RTOS
7. Linux
8. Qt和LVGL
9. 通讯协议、物联网、DSP及FatFS文件系统

本文均为TI毫米被雷达生态

毫米被雷达更像是一种SOC 集成了DSP(C6000 C674x架构的DSS)、MCU(Cortex-R4F的MSS)和雷达射频前端(BSS)
AOP系列则自带雷达射频天线
TI系列毫米被雷达均采用FMCW模式来进行雷达波的发射和采集

上电时序

毫米被雷达四个电源输入需要满足上电时序的关系
在这里插入图片描述

FMCW

使用FMCW技术 使其发射的雷达波频率一直变化
一般从基础频率开始 逐渐上升
从而分辨出不同时间发射的雷达波
根据接收的雷达波的频率 即可通过延时时间得出距离
另外 目标有速度时 通过多普勒效应 导致相位(频率)发生改变 即可实现测速
一般采用原始数据求FFT并得出range、doppler数据 从而分辨目标

使用多个RX、TX时 能够根据不同的接收数据 分辨出角度信息

雷达参数设计

雷达参数设计包括ADC采样率、TX频率、数据接收方式、帧率、TX使能、采集时间等 chirps数不算雷达射频参数之内 最终的配置决定了距离、速度、角度分辨率和最终的算法性能 最后也决定了可适用的场景和范围
同样 也应通过应用场景来反推雷达参数设计
在这里插入图片描述

工作原理

在一帧的时间周期内 TX会发送一个或多个雷达波 然后被目标反射以后通过RX接收
接收后就是数据处理和算法的计算时间
下图为TI的demo工程流程 以一帧为周期
在这里插入图片描述

数据采样

可以对雷达接收的波形进行实数、1x复数、2x复数采样
一般是采用复数采样进行 其决定了性能和信号处理方式

ADC采样

配置采样可以获取到信号的采样 每次进行ADC采样时 获得的只是雷达接收RX天线的电压大小 所以不可能获得连续的上升下降沿波形
接收一个雷达波 从上升到下降 电压有变化 采样位数就是把这个电压变化转为多少个点 2^N(也就是ADC采样率) 一般为16位采样
同时 为了得到一个频率为N的信号 一般以2N的采样位数进行采样(信号与系统)
在TI的毫米波雷达配置中 这里的ADC采样以采样位数和ADC Samples体现 就是ADC点数
另外 在数据存储方面 一般ADC原始数据以2^N对齐 比如96个ADC Samples 其一个周期的采样数据为128 但只有前96个有效

TX使能、ADC采样时间

由于发射天线TX使能时 是一个从低电平到高电平的上升沿过程
而使能时 需要过一段时间才能稳定进行采样 所以要配置TX使能时间和ADC采样时间在这里插入图片描述
如图为配置波形 只能在中间稳定部分进行采样才行
在这里插入图片描述

分辨率

毫米波雷达一般以FMCW雷达为主
FMCW,即调频连续波。FMCW技术和脉冲雷达技术是两种在高精度雷达测距中使用的技术。其基本原理为发射波为高频连续波,其频率随时间按照三角波规律变化。
使用该技术的雷达通过不同的参数配置出来的各种分辨率就不同
包括距离分辨率、速度分辨率、角度分辨率
其中 速度分辨率是靠多普勒效应来测量的 也就是频率会发生红移蓝移

以雷达测一个有长度的目标来举例:
红色的为被测物体 TX为雷达所在地方的发射天线 蓝线和绿线为雷达波
在这里插入图片描述
当雷达波第一次碰到目标时 也就是蓝波 得到一个距离d1 而被测物体有高度 所以被测物右上角反射的雷达波则为绿波
这样接收到的距离就肯定不是同一个值
如果距离分辨率不够 那么就没法测出绿波和蓝波有什么区别 就没法算出这个目标的轮廓
另外 在进行测角度时 如果角度分辨率不够 那么就没法测出物体的右上角和正前方有什么区别 也不能算出目标的轮廓
所以 应尽可能提高距离和角度分辨率以提高精确度

另外 如果目标离得近 那么不同轮廓的两个点无论是距离还是角度一定是较大的 所以在设计时 应该保证最大距离范围内也能分辨得到才行

分辨力

假设一个长度为10的物体 我需要测到两个点才能认为是一个目标 那么距离分辨率为10肯定是不行的
如果距离分辨率为5 那么有可能其中一个点刚好在目标的中间 也只能测到一个点(虽然大概率都是两个点)
一般用配置值x1.68来进行计算 比如分辨率为3 那么分辨力就是1.683=5.04 还是不能保证每距离5就有一个检测点

另外 如果目标有相对小范围的移动 多次采样是可以提高精度的 比如采样10次有9次都满足 那么算法上可以认为就被检测到了

chirps数

chirps数按理说不算雷达参数里的 但也决定了雷达的性能
所谓chirps 就是在同一帧内多次发送雷达波来进行测量
增加chirps数 可以有效增加目标的能量 并降低噪音

比如前面提到的TX的MIMO模式 就是在不同的chirps中开启不同的TX 每个TX位置不同 每个chirps测量的数据就不同 那么就可以通过算法过滤出能量最强的目标

但对于移动速度过快的目标 每个chirps测量出来的位置和结果都不一样 那么这个时候就需要缩短chirps的周期时间或者在算法上进行优化 甚至减少chirps数

所以chirps数也不是越多越好

MIMO

通过时分复用(TDM-MIMO)、频分复用(BPM-MIMO)的方式可以进行雷达TX发射
时分复用即不同时间用不同的TX去发射
在这里插入图片描述

频分复用则在同一时间用多个TX去发射(不同发射天线通过相位编码进行区分(正交))
在这里插入图片描述
由于TX位置不同 能有效提高分辨力

软件开发

可以单独使用MSS来进行开发 也可以MSS DSS协同开发

整体上采用TI-RTOS来进行开发
MSS主要负责控制、通信 而DSS主要用于算法处理

二者通过Mailbox进行信息交流

gMmwMssMCB.peerMailbox = Mailbox_open(MAILBOX_TYPE_DSS, &mboxCfg, &errCode);
Mailbox_write (gMmwMssMCB.peerMailbox, (uint8_t*)message, sizeof(MmwDemo_message));

通过Mailbox接收中断来post信号量 以此控制线程的同步 并读取Mailbox

Semaphore_pend(gMmwMssMCB.mboxSemHandle, BIOS_WAIT_FOREVER);
retVal = Mailbox_read(gMmwMssMCB.peerMailbox, (uint8_t*)&message, sizeof(MmwDemo_message));

在MSS中 同MMWave API来进行雷达配置
也可以直接移植CLI中的配置函数来进行

一个普通的开发方式是:
通过配置ADCBuf来获取雷达数据
采集到的雷达数据通过SOC监听后进入回调 并将数据通过Address Translate翻译出来
然后再获取数据

但是也可以配置Datapath(类似DMA)使其自动将数据转移

通常 数据处理都由DSS来完成 MSS只需要配置、解析DSS数据、输出

附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作

SysTick系统定时器精准延时

延时函数

SysTick->LOAD中的值为计数值
计算方法为工作频率值/分频值
比如工作频率/1000 则周期为1ms

以ADuCM4050为例:

#include "ADuCM4050.h"

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 26000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
	while(ms--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 26000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
	while(us--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

其中的52000000表示芯片的系统定时器频率 32系列一般为外部定时器频率的两倍

Cortex-M架构SysTick系统定时器阻塞和非阻塞延时

阻塞延时

首先是最常用的阻塞延时

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
	while(ms--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
	while(us--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

50000000表示工作频率
分频后即可得到不同的延时时间
以此类推

那么 不用两个嵌套while循环 也可以写成:

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器

	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
	
	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

但是这种写法有个弊端
那就是输入ms后,最大定时不得超过计数值,也就是不能超过LOAD的最大值,否则溢出以后,则无法正常工作

而LOAD如果最大是32位 也就是4294967295

晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s

固最大定时时间为85s

但用嵌套while的话 最大可以支持定时4294967295*85s

非阻塞延时

如果采用非阻塞的话 直接改写第二种方法就好了:

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器

	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
	
	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

将等待和关闭定时器语句去掉
在使用时加上判断即可变为阻塞:

delay_ms(500);
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;

在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待

不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等85s才能停下

故可以通过内部定时器来进行非阻塞延时函数的编写

基本上每个mcu的内部定时器都可以配置自动重载等功能 网上资料很多 这里就不再阐述了

位带操作

位带代码

M3、M4架构的单片机 其输出口地址为端口地址+20 输入为+16
M0架构的单片机 其输出口地址为端口地址+12 输入为+8
以ADuCM4050为列:

位带宏定义
#ifndef __GPIO_H__
#define __GPIO_H__
#include "ADuCM4050.h"
#include "adi_gpio.h"

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

#define GPIO0_ODR_Addr    (ADI_GPIO0_BASE+20) //0x40020014
#define GPIO0_IDR_Addr    (ADI_GPIO0_BASE+16) //0x40020010

#define GPIO1_ODR_Addr    (ADI_GPIO1_BASE+20) //0x40020054
#define GPIO1_IDR_Addr    (ADI_GPIO1_BASE+16) //0x40020050

#define GPIO2_ODR_Addr    (ADI_GPIO2_BASE+20) //0x40020094
#define GPIO2_IDR_Addr    (ADI_GPIO2_BASE+16) //0x40020090

#define GPIO3_ODR_Addr    (ADI_GPIO3_BASE+20) //0x400200D4
#define GPIO3_IDR_Addr    (ADI_GPIO3_BASE+16) //0x400200D0

#define P0_O(n)   	BIT_ADDR(GPIO0_ODR_Addr,n)  //输出 
#define P0_I(n)    	BIT_ADDR(GPIO0_IDR_Addr,n)  //输入 

#define P1_O(n)   	BIT_ADDR(GPIO1_ODR_Addr,n)  //输出 
#define P1_I(n)    	BIT_ADDR(GPIO1_IDR_Addr,n)  //输入 

#define P2_O(n)   	BIT_ADDR(GPIO2_ODR_Addr,n)  //输出 
#define P2_I(n)    	BIT_ADDR(GPIO2_IDR_Addr,n)  //输入 

#define P3_O(n)   	BIT_ADDR(GPIO3_ODR_Addr,n)  //输出 
#define P3_I(n)    	BIT_ADDR(GPIO3_IDR_Addr,n)  //输入 

#define Port0			(ADI_GPIO_PORT0)
#define Port1			(ADI_GPIO_PORT1)
#define Port2			(ADI_GPIO_PORT2)
#define Port3			(ADI_GPIO_PORT3)

#define Pin0			(ADI_GPIO_PIN_0)
#define Pin1			(ADI_GPIO_PIN_1)
#define Pin2			(ADI_GPIO_PIN_2)
#define Pin3			(ADI_GPIO_PIN_3)
#define Pin4			(ADI_GPIO_PIN_4)
#define Pin5			(ADI_GPIO_PIN_5)
#define Pin6			(ADI_GPIO_PIN_6)
#define Pin7			(ADI_GPIO_PIN_7)
#define Pin8			(ADI_GPIO_PIN_8)
#define Pin9			(ADI_GPIO_PIN_9)
#define Pin10			(ADI_GPIO_PIN_10)
#define Pin11			(ADI_GPIO_PIN_11)
#define Pin12			(ADI_GPIO_PIN_12)
#define Pin13			(ADI_GPIO_PIN_13)
#define Pin14			(ADI_GPIO_PIN_14)
#define Pin15			(ADI_GPIO_PIN_15)

void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag);
void GPIO_BUS_OUT(unsigned int port,unsigned int num);

void P0_BUS_O(unsigned int num);
unsigned int P0_BUS_I(void);

void P1_BUS_O(unsigned int num);
unsigned int P1_BUS_I(void);

void P2_BUS_O(unsigned int num);
unsigned int P2_BUS_I(void);

void P3_BUS_O(unsigned int num);
unsigned int P3_BUS_I(void);

#endif

总线函数
#include "ADuCM4050.h"
#include "adi_gpio.h"
#include "GPIO.h"

void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag)
{
	switch(port)
	{
		case 0:{
			switch(pin)
			{
				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));};break;
				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));};break;
				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));};break;
				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));};break;
				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));};break;
				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));};break;
				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));};break;
				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));};break;
				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));};break;
				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));};break;
				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));};break;
				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));};break;
				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));};break;
				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));};break;
				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));};break;
				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));};break;
				default:pin=0;break;
			}
		}break;
		
		case 1:{
			switch(pin)
			{
				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));};break;
				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));};break;
				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));};break;
				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));};break;
				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));};break;
				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));};break;
				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));};break;
				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));};break;
				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));};break;
				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));};break;
				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));};break;
				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));};break;
				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));};break;
				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));};break;
				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));};break;
				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));};break;
				default:pin=0;break;
			}
		}break;
		
		case 2:{
			switch(pin)
			{
				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));};break;
				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));};break;
				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));};break;
				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));};break;
				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));};break;
				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));};break;
				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));};break;
				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));};break;
				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));};break;
				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));};break;
				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));};break;
				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));};break;
				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));};break;
				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));};break;
				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));};break;
				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));};break;
				default:pin=0;break;
			}
		}break;
		
		case 3:{
			switch(pin)
			{
				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));};break;
				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));};break;
				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));};break;
				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));};break;
				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));};break;
				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));};break;
				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));};break;
				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));};break;
				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));};break;
				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));};break;
				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));};break;
				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));};break;
				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));};break;
				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));};break;
				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));};break;
				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));};break;
				default:pin=0;break;
			}
		}break;
		
		default:port=0;break;
	}	
}

void GPIO_BUS_OUT(unsigned int port,unsigned int num)  //num最大为0xffff
{
	int i;
	for(i=0;i<16;i++)
	{
		GPIO_OUT(port,i,(num>>i)&0x0001);
	}
}


void P0_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		P0_O(i)=(num>>i)&0x0001;
	}
}
unsigned int P0_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(P0_I(i)<<i)&0xFFFF;
	}
	return num;
}

void P1_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		P1_O(i)=(num>>i)&0x0001;
	}
}
unsigned int P1_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(P1_I(i)<<i)&0xFFFF;
	}
	return num;
}

void P2_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		P2_O(i)=(num>>i)&0x0001;
	}
}
unsigned int P2_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(P2_I(i)<<i)&0xFFFF;
	}
	return num;
}

void P3_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		P3_O(i)=(num>>i)&0x0001;
	}
}
unsigned int P3_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(P3_I(i)<<i)&0xFFFF;
	}
	return num;
}

一、位带操作理论及实践

位带操作的概念其实30年前就有了,那还是 CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版

位带区: 支持位带操作的地址区

位带别名: 对别名地址的访问最终作 用到位带区的访问上(注意:这中途有一个 地址映射过程)

位带操作对于硬件 I/O 密集型的底层程序最有用处

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM4中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

位操作就是可以单独的对一个比特位读和写,类似与51中sbit定义的变量,stm32中通过访问位带别名区来实现位操作的功能
STM32中有两个地方实现了位带,一个是SRAM,一个是片上外设。
在这里插入图片描述
(1)位带本质上是一块地址区(例如每一位地址位对应一个寄存器)映射到另一片地址区(实现每一位地址位对应一个寄存器中的一位),该区域就叫做位带别名区,将每一位膨胀成一个32位的字。
(2)位带区的4个字节对应实际寄存器或内存区的一个位,虽然变大到4个字节,但实际上只有最低位有效(代表0或1)

只有位带可以直接用=赋值的方式来操作寄存器 位带是把寄存器上的每一位 膨胀到32位 映射到位带区 比如0x4002 0000地址的第0个bit 映射到位带区的0地址 那么其对应的位带映射地址为0x00 - 0x04 一共32位 但只有LSB有效 采用位带的方式用=赋值时 就是把位带区对应的LSB赋值 然后MCU再转到寄存器对应的位里面 寄存器操作时 如果不改变其他位上面的值 那就只能通过&=或者|=的方式进行

在这里插入图片描述

要设置0x2000 0000这个字节的第二个位bit2为1,使用位带操作的步骤有:
1、将1写入位 带别名区对应的映射地址(即0x22000008,因为1bit对应4个byte);
2、将0x2000 0000的值 读取到内部的缓冲区(这一步骤是内核完成的,属于原子操作,不需要用户操作);
3、将bit2置1,再把值写 回到0x2000 0000(属于原子操作,不需要用户操作)。

关于GPIO引脚对应的访问地址,可以参考以下公式
寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

如:端口F访问的起始地址GPIOF_BASE

#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)

在这里插入图片描述

但好在官方库里面都帮我们定义好了 只需要在BASE地址加上便宜即可

例如:

GPIOF的ODR寄存器的地址 = GPIOF_BASE + 0x14

寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

设置PF9引脚的话:

uint32_t *PF9_BitBand =
*(uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR– 0x40000000) *32 + 9*4)

封装一下:

#define PFout(x) *(volatile uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR – 0x40000000) *32 + x*4)

现在 可以把通用部分封装成一个小定义:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

那么 设置PF引脚的函数可以定义:

#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414   
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 

#define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

若使PF9输入输出则:

PF_O(9)=1;  //输出高电平
uint8_t dat = PF_I(9);  //获取PF9引脚的值

总线输入输出:

void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PF_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PF_I(i)<<i)&0xFFFF;
	}
	return num;
}

STM32的可用下面的函数:

#ifndef __GPIO_H__
#define __GPIO_H__
#include "stm32l496xx.h"

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
#define PA_O(n)   	BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PA_I(n)    	BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PB_O(n)   	BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PB_I(n)    	BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PC_O(n)   	BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PC_I(n)    	BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PD_O(n)   	BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PD_I(n)    	BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PE_O(n)   	BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PE_I(n)    	BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PG_O(n)   	BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PG_I(n)    	BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PH_O(n)   	BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PH_I(n)    	BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PI_O(n)			BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PI_I(n)   	BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

void PA_BUS_O(unsigned int num);
unsigned int PA_BUS_I(void);

void PB_BUS_O(unsigned int num);
unsigned int PB_BUS_I(void);

void PC_BUS_O(unsigned int num);
unsigned int PC_BUS_I(void);

void PD_BUS_O(unsigned int num);
unsigned int PD_BUS_I(void);

void PE_BUS_O(unsigned int num);
unsigned int PE_BUS_I(void);

void PF_BUS_O(unsigned int num);
unsigned int PF_BUS_I(void);

void PG_BUS_O(unsigned int num);
unsigned int PG_BUS_I(void);

void PH_BUS_O(unsigned int num);
unsigned int PH_BUS_I(void);

void PI_BUS_O(unsigned int num);
unsigned int PI_BUS_I(void);

#endif

#include "GPIO.h"

void PA_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PA_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PA_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PA_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PB_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PB_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PB_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PB_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PC_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PC_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PC_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PC_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PD_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PD_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PD_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PD_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PE_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PE_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PE_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PE_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PF_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PF_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PG_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PG_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PG_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PG_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PH_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PH_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PH_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PH_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PI_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PI_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PI_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PI_I(i)<<i)&0xFFFF;
	}
	return num;
}

二、如何判断MCU的外设是否支持位带

根据《ARM Cortex-M3与Cortex-M4权威指南(第3版)》中第6章第7节描述
在这里插入图片描述
也就是说 要实现对GPIO的位带操作 必须保证GPIO位于外设区域的第一个1MB中
第一个1MB应该是0x4010 0000之前 位带不是直接操作地址 而是操作地址映射 地址映射被操作以后 MCU自动会修改对应寄存器的值

位带区只有1MB 所以只能改0x4000 0000 - 0x400F FFFF的寄存器
像F4系列 GPIO的首地址为0x4002 0000 就可以用位带来更改

STM32L476的GPIO就不行:
在这里插入图片描述
AHB2的都不能用位带
ABP 还有AHB1都可以用
在这里插入图片描述
但是L476的寄存器里面 GPIO和ADC都是AHB2

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式拳铁编曲MikeZhou

光电帝国,光联万物!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值