首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【STM32】HAL库Flash读写操作及配置(L4和F4系列不同操作、HAL_FLASH_ERROR_PGA报错的解决方案)

  • 25-02-16 16:41
  • 3296
  • 12882
blog.csdn.net

【STM32】HAL库Flash读写操作及配置(L4和F4系列不同操作、HAL_FLASH_ERROR_PGA报错的解决方案)

文章目录

  • Flash读写
    • 解锁、上锁和等待
    • L4系列Flash操作
      • 擦除
      • 写入
    • F4系列Flash操作
      • 擦除
      • 写入
  • HAL_FLASH_ERROR_PGA报错
  • 读数据
  • 附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作
    • SysTick系统定时器精准延时
      • 延时函数
        • 阻塞延时
        • 非阻塞延时
    • 位带操作
      • 位带代码
        • 位带宏定义
        • 总线函数
      • 一、位带操作理论及实践
      • 二、如何判断MCU的外设是否支持位带

Flash读写

在这里插入图片描述
在keil里面的默认工程配置中 Flash分配地址 程序部分为0x0800 0000到0x0810 0000 总共是0x0010 0000的大小 也就是1048576Byte 1024KB 1MB

而实际上程序部分大小应该要看硬件手册来确定

可以通过配置keil工程中size的大小 来确定程序地址的范围(如果太小了 编译会报错) 在进行flash读写时 应跳过程序部分

在用户手册里 可以查看flash的分配情况
在这里插入图片描述
如STM32L496xx 有两个banks 每个bank有256页 每一页对应2KB 一共是1MB
(其他系列的MCU就不一样 有的没有Page这个概念 而是用SECTOR 譬如STM32F407系列)
在这里插入图片描述
L47x、L48x、L49x、L4Ax系列还有512KB、256KB的产品 其Page的排列不是连续的 同时DUALBANK为1时表示使用两个Bank
在这里插入图片描述

在这里插入图片描述
其DUALBANK位控制是使用单BANK还是双BANK
在这里插入图片描述

同样 有的MCU有两种操作方式 如L4R、L4S系列分为双存储和单存储两种 由DBANK控制 前者是64位宽度读写 后者是128位读写
在这里插入图片描述
在这里插入图片描述

我的程序编译后 大概占用了24192的空间 也就要跳过这个空间才能进行读写操作

在这里插入图片描述

基本上 大小选择为0x10000也是不会报错的
在这里插入图片描述
那进行flash操作就可以从0x0801 0000开始
一直到0x080F FFFF结束

Flash的读写与EEPROM不同 读的时候可以随时读 但写的时候不能直接写 必须先将该页2KB的数据清除 然后再写(清除后 该页的数据全为1)
注意:要明白Flash的编程原理都是只能将1写为0,而不能将0写为1,所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。

解锁、上锁和等待

在每次操作Flash前 需要解锁
操作完毕后 则需要上锁

HAL_FLASH_Unlock();    //解锁
HAL_FLASH_Lock();      //上锁
  • 1
  • 2

另外 由于Flash操作是以页、扇区为单位 所以每次写入前 都要以页、扇区为单位进行擦除操作
擦除后 不需要上锁 然后才可以进行写操作 写操作完毕后上锁 这就是一次完整的Flash写入周期
在擦除和调用写入函数前 可以调用FLASH_WaitForLastOperation(0xFFFF);函数进行等待
如果没有进行擦除就写入的话 就可能报错 就算不报错 数据也是写不进去的
所以每次都要先进行擦除 再进行写入
如果数据量太大 可以先读取 对比一下是不是一样的数据 然后再选择进行擦除和写入 如果数据一样 就不需要写入了

L4系列Flash操作

操作L496的话 是双字64位操作
在这里插入图片描述
在双Bank模式下 每次擦除时还需要选择擦除的Bank序号(1或2 或两者都擦除)

/** @defgroup FLASH_Banks FLASH Banks
  * @{
  */
#define FLASH_BANK_1              ((uint32_t)0x01)                          /*!< Bank 1   */
#if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
    defined (STM32L496xx) || defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || defined (STM32L4R5xx) || \
    defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
#define FLASH_BANK_2              ((uint32_t)0x02)                          /*!< Bank 2   */
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1 | FLASH_BANK_2)) /*!< Bank1 and Bank2  */
#else
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1))                /*!< Bank 1   */
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

HAL库测试代码如下:

void Test_Flash(uint32_t add)
{
	uint32_t error = 0;
	uint64_t dat = 0x0123456776543210;//要写入的数据,必须得是双字64bit
	uint64_t read_dat = 0 ;
	FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
	
	HAL_FLASH_Unlock();                                    //第二步:解锁                        
	flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
	flash_dat.Page = (uint32_t)((add-0x08000000)/2048);            //擦除地址对应的页
	flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页
	if(flash_dat.Page>255)
	{
		flash_dat.Banks=2;
	}
	else
	{
		flash_dat.Banks=1;
	}
	HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
	FLASH_WaitForLastOperation(0xFFFF); 
	HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);//第四步:写入数据
	HAL_FLASH_Lock();                                      //第五步:上锁
	
	read_dat = *(__I uint64_t *)add;	   //读出flash中的数据
	uint32_t read_dat1=read_dat>>32;
	uint32_t read_dat2=read_dat&0x00000000FFFFFFFF;
	printf("[INFO] Flash_Test:0x%08x 0x%08x\n",read_dat1,read_dat2);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

步骤就是:
解锁;
擦除;
写数据;
上锁。

擦除

直接按页进行擦除即可

	FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
	
	HAL_FLASH_Unlock();                                    //第二步:解锁                        
	flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
	flash_dat.Page = (uint32_t)((add-0x08000000)/2048);            //擦除地址对应的页
	flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页
	flash_dat.Banks=1;
	HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

写入

以双字为对齐进行写入

HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);//第四步:写入数据
  • 1

F4系列Flash操作

注意 不同的芯片扇区分区不一样 譬如F407系列(这里截图的是极海的F407) 扇区大小前面几个都是16k 后面则是64k 128k
在这里插入图片描述
那么就需要修改flash_dat.Page以及其他参数
在HAL库中 FLASH_EraseInitTypeDef 的定义完全不一样
这里是用扇区SECTOR来进行操作的 而不是页Page
擦除类型则变成了FLASH_TYPEERASE_SECTORS
另外还有一个电压选择VoltageRange
根据MCU本身来进行配置

#define FLASH_VOLTAGE_RANGE_1        0x00000000U  /*!< Device operating range: 1.8V to 2.1V                */
#define FLASH_VOLTAGE_RANGE_2        0x00000001U  /*!< Device operating range: 2.1V to 2.7V                */
#define FLASH_VOLTAGE_RANGE_3        0x00000002U  /*!< Device operating range: 2.7V to 3.6V                */
#define FLASH_VOLTAGE_RANGE_4        0x00000003U  /*!< Device operating range: 2.7V to 3.6V + External Vpp */
  • 1
  • 2
  • 3
  • 4

一般就是FLASH_VOLTAGE_RANGE_3

测试函数:

void Test_Flash(void)
{
  uint32_t add = 0x08020000;        //定义写入数据的地址
	uint32_t error = 0;
	uint64_t dat_64 = 0x0123456776543210;//要写入的数据,必须得是双字64bit
	uint32_t dat_32 = 0x01234567;
	uint64_t read_dat = 0 ;

	FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
	
	HAL_FLASH_Unlock();                                    //第二步:解锁                        
	flash_dat.TypeErase = FLASH_TYPEERASE_SECTORS;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
	flash_dat.Sector = FLASH_SECTOR_5;            //擦除地址对应的页
	flash_dat.NbSectors = 1;                               //一次性擦除1页,可以是任意页
	flash_dat.Banks=FLASH_BANK_1;
	flash_dat.VoltageRange=FLASH_VOLTAGE_RANGE_3;
	HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
	FLASH_WaitForLastOperation(0xFFFF); 
	error = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, add, dat_32);//第四步:写入数据
	HAL_FLASH_Lock();                                      //第五步:上锁
	
	read_dat = *(__I uint64_t *)add;	   //读出flash中的数据
	uint32_t read_dat1=read_dat>>32;
	uint32_t read_dat2=read_dat&0x00000000FFFFFFFF;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

擦除

	FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
	
	HAL_FLASH_Unlock();                                    //第二步:解锁                        
	flash_dat.TypeErase = FLASH_TYPEERASE_SECTORS;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
	flash_dat.Sector = FLASH_SECTOR_5;            //擦除地址对应的页
	flash_dat.NbSectors = 1;                               //一次性擦除1页,可以是任意页
	flash_dat.Banks=FLASH_BANK_1;
	flash_dat.VoltageRange=FLASH_VOLTAGE_RANGE_3;
	HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

写入

写入时 TypeProgram很有讲究 如果填错了则触发HAL_FLASH_ERROR_PGA报错

error = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, add, dat_32);//第四步:写入数据
	HAL_FLASH_Lock();                                      //第五步:上锁
  • 1
  • 2

这里TypeProgram是根据工作电压来的 上面四种工作电压对应状态就是对应8、16、32、64的TypeProgram
所以不能填错

在FLASH_Erase_Sector函数说明中可以看到:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那么在一般情况下 都是用FLASH_TYPEPROGRAM_WORD

HAL_FLASH_ERROR_PGA报错

出现此报错就是未对齐
在这里插入图片描述
更改其他对齐方式即可

读数据

最简单的读数据就用:

read_dat = *(__I uint64_t *)add;
  • 1

并不需要什么HAL库

__I
表示read only

测试结果如下:
在这里插入图片描述

debug调试界面结果
在这里插入图片描述

有个需要注意的地方 那就是Flash擦写次数为1w次 所以测试的时候千万不能放在死循环下面

附录: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  关闭系统定时器
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

其中的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  关闭系统定时器
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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  关闭系统定时器
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

但是这种写法有个弊端
那就是输入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  关闭系统定时器
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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

delay_ms(500);
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;
  • 1
  • 2
  • 3

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

不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
总线函数
#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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190

一、位带操作理论及实践

位带操作的概念其实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)

  • 1
  • 2
  • 3

封装一下:

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

  • 1
  • 2

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

#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))
  • 1
  • 2
  • 3

那么 设置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)  //输入
  • 1
  • 2
  • 3
  • 4
  • 5

若使PF9输入输出则:

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

总线输入输出:

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
#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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173

二、如何判断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

电子信息类交流
QQ群名片
注:本文转载自blog.csdn.net的嵌入式拳铁编曲MikeZhou的文章"https://blog.csdn.net/weixin_53403301/article/details/128575591"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top