当前位置: 首页 > news >正文

网站建设公司专业的建站优化公司wordpress 调用 置顶

网站建设公司专业的建站优化公司,wordpress 调用 置顶,WordPress调用外链,seo推广是什么意怿目录 一、STM32芯片架构简图及系统框图 1.1 STM32芯片架构简图 1.1.1 FLASH是什么#xff0c;用来做什么 1.1.2 SRAM是什么#xff0c;用来做什么 1.1.3 片上外设是什么#xff0c;用来做什么 1.2 系统框图 1.2.1 驱动单元 1.2.2 被动单元 二、什么是寄存器 2.1 存…目录 一、STM32芯片架构简图及系统框图 1.1 STM32芯片架构简图 1.1.1 FLASH是什么用来做什么 1.1.2 SRAM是什么用来做什么 1.1.3 片上外设是什么用来做什么 1.2 系统框图 1.2.1 驱动单元 1.2.2 被动单元 二、什么是寄存器 2.1 存储器映射 2.1.1 存储器 Block0 内部区域功能划分 ​编辑 2.1.2 存储器 Block1 内部区域功能划分 2.1.3 存储器 Block2 内部区域功能划分 2.2寄存器映射 2.2.1 STM32的外设地址映射 2.2.2 C语言对寄存器的封装 2.2.3 修改寄存器的位操作方法 注意 文中标注 红色是重要信息绿色是个人总结 一、STM32芯片架构简图及系统框图 1.1 STM32芯片架构简图 STM32 芯片是已经封装好的成品主要由内核和片上外设组成。若与电脑类比内核与外设就如同电脑上的 CPU 与主板、内存、显卡、硬盘的关系。         STM32F103 采用的是 Cortex-M3 内核内核即 CPU由 ARM 公司设计。ARM 公司并不生产芯片而是出售其芯片技术授权。芯片生产厂商(SOC)如ST、TI、Freescale负责在内核之外设计部件并生产整个芯片这些内核之外的部件被称为核外外设或片上外设。如GPIO、USART串口、I2C、SPI等都叫做片上外设。 架构简图  个人总结STM32单片机有很多系列它其中的内核包括很多有M0、M3、M4、M7等等各种内核这些内核是由 ARM 公司进行设计但不生产 相当于盖房子是设计图纸的而ST、TI等这些公司来进行生产 相当于拿到图纸进行盖房子的建筑公司。他们不仅盖房子而且还在房子旁边建筑了配套设施这可以看作是片上外设。 1.1.1 FLASH是什么用来做什么 FLASH是什么 FLASH: Flash memory闪存用于存储程序代码和数据。特性非易失性存储器系统断电或重启时存储在其中的数据不会丢失。用途用于存储用户程序、固件和其他必要的信息。 在STM32芯片中FLASH 的主要作用用来做什么包括 存储程序代码 Flash主要用于存储微控制器的程序代码。在开发STM32应用时您的编程代码将被烧录到Flash存储器中以便微控制器能够执行您的应用程序。非易失性存储 与RAM随机访问存储器不同Flash是非易失性存储器这意味着它可以在断电后保持存储内容。这使得Flash成为存储在微控制器上电之间需要保留的重要信息的理想选择。固件升级 Flash还用于执行固件升级。通过在Flash中存储新的固件版本您可以通过编程方式更新微控制器的软件而无需更换芯片。数据存储 除了程序代码Flash还可用于存储一些常量数据、配置信息或其他需要在应用程序之间保持不变的数据。 1.1.2 SRAM是什么用来做什么 SRAM 是什么  SRAMStatic Random-Access Memory静态随机访问存储器是一种用于存储数据的类型的内存。特性易失性存储器系统断电或重启时存储在其中的数据会丢失。用途Flash存储器通常用于存储程序代码而 SRAM 则用于存储临时变量、中间计算结果和其他需要在程序执行期间频繁读写的数据。 在STM32芯片中SRAM的主要作用用来做什么包括 数据存储 SRAM用于存储在程序执行期间生成的数据。这可以包括临时变量、堆栈信息、全局变量等。堆栈操作 堆栈是一种用于存储函数调用和返回地址的数据结构。在程序执行时每次函数调用都会将一些信息如局部变量、返回地址等存储在堆栈中。由于堆栈的访问模式是先进后出Last In First OutLIFO因此SRAM是一个适合堆栈操作的存储介质。运行时堆管理 一些应用程序可能需要在运行时动态分配内存例如使用malloc和free等函数。这样的内存分配通常是从SRAM中进行的。中间结果存储 在一些计算密集型应用中SRAM用于存储中间计算结果以提高访问速度并减少对Flash存储器的写入次数。 总体而言SRAM在STM32芯片中起到了关键的作用对于实时性要求高、需要频繁读写数据的应用程序来说SRAM的快速访问速度和可读写特性是非常重要的。 个人总结SRAM 就是一块儿内存可以高速访问读写的内存但掉电丢失数据相当于我们手机里经常说的内存 8G128G 中的 8G我们打开某个APP手机的操作系统就会调用该APP其中产生的临时变量、函数调用什么的都是在运行内存上进行所以买手机大家都看运行内存有多大当然越大你的手机使用起来就越流畅开多个应用也不会卡当然这也只是你手机运行流畅的一部分原因还得看手机厂商做的怎么样之类的。 1.1.3 片上外设是什么用来做什么 在 STM32 微控制器中片上外设On-Chip Peripherals指的是集成在芯片内部的硬件功能模块用于执行特定的任务或提供特定的功能。这些外设可以大大简化嵌入式系统的设计因为它们无需外部组件就能执行各种任务。 STM32 系列微控制器的片上外设种类繁多不同型号的芯片可能具有不同的外设配置。以下是一些常见的 STM32片 上外设以及它们的一些用途 GPIO通用输入/输出 用于连接和控制外部数字设备例如LED、按钮、传感器等。USART/UART通用同步/异步收发器 用于串行通信例如连接到计算机、传感器、其他微控制器等。SPI串行外设接口 用于高速串行通信适用于连接存储器、传感器、显示屏等。I2CInter-Integrated Circuit 用于短距离串行通信适用于连接多个设备如传感器、EEPROM等。ADC模数转换器 用于将模拟信号转换为数字信号适用于从传感器中读取模拟数据。PWM脉冲宽度调制 用于生成脉冲信号通常用于控制电机、LED亮度等。定时器和计数器 用于生成定时事件执行精确的时间控制。看门狗定时器WDT 用于监视系统的运行状况防止系统死锁或崩溃。CAN控制器区域网络 用于在车辆系统等应用中进行高速通信。USB控制器 用于支持USB通信。 这只是其中一部分外设的示例不同的STM32型号可能会包含其他特定用途的外设。使用这些片上外设开发人员可以更轻松地实现各种应用而无需外部硬件。 1.2 系统框图 芯片这里指内核或者叫CPU和外设之间通过各种总线连接其中驱动单元有 4 个被动单元也有 4 个具体见图 STM32F10xx 系统框图。为了方便理解我们都可以把驱动单元理解成是 CPU 部分被动单元都理解成外设。下面我们简单介绍下驱动单元和被动单元的各个部件。 系统框图 1.2.1 驱动单元 1ICode 总线 ICode 中的 I 表示 Instruction即指令。我们写好的程序编译之后都是一条条指令存放在FLASH 中内核要读取这些指令来执行程序就必须通过 ICode 总线它几乎每时每刻都需要被使用它是专门用来取指的。 个人总结我们编辑好我们的代码经过编译器编译生成计算机可执行的代码。这些代码我们可以通过反汇编查看其实就是一条条的指令计算机可以看懂的代码指令这些指令都存储在我们FLASH上CPU 通过 ICode 总线读取指令进行操作。 2DCode 总线 DCode 中的 D 表示 Data即数据那说明这条总线是用来取数的。 我们在写程序的时候数据有常量和变量两种常量就是固定不变的用 C 语言中的 const 关键字修饰是放到内部FLASH 当中的 变量是可变的不管是全局变量还是局部变量都放在内部的SRAM。 因为数据可以被 Dcode 总线和 DMA 总线访问所以为了避免访问冲突在取数的时候需要经过一个总线矩阵来仲裁决定哪个总线在取数。 在C语言中全局变量通常存放在静态存储区不是堆上也不是栈上。具体来说全局变量存放在程序的 数据段 或 BSS段 中这是程序的静态存储区域。 数据段 如果全局变量被初始化它们的值将存储在数据段中。数据段的大小在编译时确定包含所有已初始化的全局变量。BSS段 如果全局变量未被初始化它们的值将被初始化为零并存储在BSS段中。BSS段通常在可执行文件中占据一些空间但不存储实际的数据因为这些变量被默认初始化为零。         在函数内声明的局部变量通常存储在栈上而动态分配的内存通过malloc、calloc等函数分配的存储在堆上。全局变量的生命周期通常是整个程序的运行时间而局部变量的生命周期则与其所在的函数调用有关。         需要注意的是全局变量和静态变量在函数内使用static关键字声明的变量都在程序的静态存储区域因此它们的生命周期是整个程序的运行时间。 3System 系统总线 系统总线主要是访问外设的寄存器我们通常说的寄存器编程即读写寄存器都是通过这根系统总线来完成的。 4DMA 总线 DMA 总线也主要是用来传输数据这个数据可以是在某个外设的数据寄存器可以在SRAM可以在内部的 FLASH 。因为数据可以被 Dcode 总线和 DMA 总线访问所以为了避免访问冲突在取数的时候需要经过一个总线矩阵来仲裁决定哪个总线在取数。 个人总结它允许片上外设直接访问计算机内存而不需要经过中央处理单元CPU的干预。这样可以在数据传输过程中解放 CPU使其能够执行其他任务从而提高系统的整体性能数据在DMA控制器下进行的数据传输过程中CPU 可以继续执行其他指令而不必等待数据传输的完成不受数据传输过程的干扰。 1.2.2 被动单元 1内部的闪存存储器 —— Flash 内部的闪存存储器即 FLASH我们编写好的程序就放在这个地方。内核通过 ICode 总线来取里面的指令。我们程序中生成的一些重要数据也可以保存在芯片内部的flash中但要注意读写空间不要把 程序运行的代码给篡改了。 2内部的SRAM 内部的SRAM即我们通常说的RAM程序的变量堆栈等的开销都是基于内部的SRAM。内核通过 DCode 总线来访问它。 3静态的存储器控制器 —— FSMC FSMC的英文全称是Flexiblestaticmemorycontroller叫灵活的静态的存储器控制器是STM32F10xx中一个很有特色的外设通过FSMC我们可以扩展内存如外部的SRAMNAND-FLASH和NORFLASH。但有一点我们要注意的是FSMC只能扩展静态的内存即名称里面的Sstatic不能是动态的内存比如SDRAM就不能扩展。 4AHB 到 APB 的桥 从 AHB 总线延伸出来的两条 APB2 和 APB1 总线上面挂载着STM32各种各样的特色外设。我们经常说的 GPIO、串口、I2C、SPI 这些外设就挂载在这两条总线上这个是我们学习STM32的重点就是要学会编程这些外设去驱动外部的各种设备。 二、什么是寄存器 2.1 存储器映射 在图 STM32F10xx 系统框图中被控单元的 FLASHRAMFSMC 和 AHB 到 APB 的桥即片上外设这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候可以通过他们的地址找到他们然后来操作他们通过C语言对它们进行数据的读和写。 个人总结为什么是一个 4GB 的地址空间呢因为STM32是32位的处理器32位架构使用32位的地址总线。地址总线的位数决定了处理器能够寻址的内存地址数量。在32位系统中地址总线的宽度为32位这意味着它可以产生2的32次方个不同的地址。比如 0x0000 0000  ~ 0xFFFF FFFF 每个地址对应一个字节的内存。因此2^{32}个地址对应的总字节数为2^{32}字节即4GB。这就是为什么32位处理器的地址空间限制在4GB的原因。 需要注意的是32 位处理器的地址空间中并不是所有的 4GB 都用于访问物理内存。一部分地址空间可能被保留用于系统和硬件的目的例如I/O端口、内核空间等。实际可用的用户空间内存可能会略小于4GB。 存储器本身不具有地址信息它的地址是由芯片厂商或用户分配给存储器分配地址的过程就称为 —— 存储器映射具体见图存储器映射。如果给存储器再分配一个地址就叫 —— 存储器重映射。 在这 4GB 的地址空间中ARM 已经粗线条的平均分成了8个块每块512MB每个块也都规定了用途具体分类见表格存储器功能分类。每个块的大小都有512MB显然这是非常大的芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完都是只用了其中的一部分而已。 在这8个 Block 里面有3个块非常重要也是我们最关心的三个块。Block0 用来设计成内部FLASHBlock1 用来设计成内部 RAMBlock2 用来设计成片上的外设下面我们简单的介绍下这三个Block里面的具体区域的功能划分。  个人总结这里所说的 4GB 的地址空间应该说的是理论上可以有这么多的地址空间但实际设计设计生产中不可能开辟这么大一块地址空间一个地址对应一个字节的数据存储空间比如0x2000 0000 这是一个地址如果要生产设计出实体芯片来这个地址就实际对应着可以存储一个字节的内存工厂生产要考虑实际的应用因此根据自己所需拿出一部分地址空间出来进行实际的设计生产。 2.1.1 存储器 Block0 内部区域功能划分 Block0 主要用于设计片内的 FLASH我们使用的 STM32F103ZET6 的 FLASH 都是512KB属于大容量。要在芯片内部集成更大的 FLASH 或者 SRAM 都意味着芯片成本的增加往往片内集成的 FLASH 都不会太大。Block 内部区域的功能划分具体见表格存储器 Block0 内部区域功能划分。 2.1.2 存储器 Block1 内部区域功能划分 Block1 用于设计片内的 SRAM 。我们使用的 STM32F103ZET6 的 SRAM 都是64KBBlock内部区域的功能划分具体见表格存储器 Block1 内部区域功能划分。 2.1.3 存储器 Block2 内部区域功能划分 Block2 用于设计片内的外设根据外设的总线速度不同Block 被分成了 APB 和 AHB 两部分其中 APB 又被分为 APB1 和 APB2具体见表格存储器 Block2 内部区域功能划分。 2.2寄存器映射 我们知道存储器本身没有地址给存储器分配地址的过程叫存储器映射那什么叫寄存器映射寄存器到底是什么         在存储器 Block2 这块区域设计的是片上外设它们以四个字节为一个单元共 32bit每一个单元对应不同的功能当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址然后通过 C 语言指针的操作方式来访问这些单元如果每次都是通过这种地址的方式来访问不仅不好记忆还容易出错这时我们可以根据每个单元功能的不同以功能为名给这个内存单元取一个别名这个别名就是我们经常说的 —— 寄存器这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。 个人总结这里就可以理解为寄存器是实体存在在片上外设给一块四个字节的存储空间命名为一个特殊的名字这个就是寄存器。四个字节为一个单元共 32bit每一个单元对应不同的功能怎么理解呢就还是房子比如城市里的房子四个字节为一个整体看作是一个小区一栋楼里的一户人家这一户有自己的工作政府办公大楼相当于是CPUCPU每次找这户人家干活时总得有个名字吧比如这一户是 301那么 301就是寄存器CPU每次只需要找到301就可以让301干一些301可以干的活。         比如我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x40010C0C至于这个地址如何找到可以先跳过后面我们会有详细的讲解ODR 寄存器是32bit低 16bit 有效对应着16 个外部 IO写 0/1 对应的的 IO 则输出低/高电平。现在我们通过C语言指针的操作方式让GPIOB 的 16 个 IO 都输出高电平具体见下图代码 //GPIOB端口全部输出高电平 *(unsigned int*)(0x40010C0C) 0xFFFF; 个人总结这里的0xFF就相当于你这户人家可以干的活儿可以干0x00-0xFF0x40010C0C 就当与是你家房子的原始编号政府(CPU人员找你家办事很不方便所以需要将你家这个原始编号进行重新命名。 0x40010C0C 在我们看来是 GPIOB 端口 ODR 的地址但是在编译器看来这只是一个普通的变量是一个立即数要想让编译器也认为是指针我们得进行强制类型转换把它转换成指针即  (unsigned int*)0x40010C0C然后再对这个指针进行 * 操作。 刚刚我们说了通过绝对地址访问内存单元不好记忆且容易出错我们可以通过寄存器的方式来操作具体见下图代码 //GPIOB端口全部输出高电平 #define GPIOB_ODR (unsigned int*)(GPIOB_BASE 0x0C) *GPIOB_ODR 0xFF; 为了方便操作我们干脆把指针操作  “*”  也定义到寄存器别名里面具体见下图代码 //GPIOB端口全部输出高电平 #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE 0x0C) GPIOB_ODR 0xFF; 个人总结这里的 GPIO_ODR 相当于你家的 301就是寄存器以后政府找你也方便了。以下的STM32的外设地址映射也是同样的道理每家每户都有自己可以干的特殊工作因此给每家每户重新命名一次。 2.2.1 STM32的外设地址映射 片上外设区分为三条总线根据外设速度的不同不同总线挂载着不同的外设APB1 挂载低速外设APB2 和 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址总线基地址也是挂载在该总线上的首个外设的地址。其中 APB1 总线的地址最低片上外设从这里开始也叫外设基地址。 1总线基地址 表格总线基地址的“相对外设基地址偏移”即该总线地址与“片上外设”基地址 0x40000000 的差值。 2外设基地址 总线上挂载着各种外设这些外设也有自己的地址范围特定外设的首个地址称为“XX外设基地址”也叫XX外设的边界地址。具体有关 STM32F10xx 外设的边界地址请参考《STM32F10xx 参考手册》的2.3小节的存储器映射的表1STM32F10xx寄存器边界地址。         这里面我们以 GPIO 这个外设来讲解外设的基地址GPIO 属于高速的外设挂载到APB2总线上具体见表格外设 GPIO 基地址。 3外设寄存器 在XX外设的地址范围内分布着的就是该外设的寄存器。以 GPIO 外设为例GPIO 是通用输入输出端口的简称简单来说就是 STM32 可控制的引脚基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把 GPIO 的引脚连接到 LED 灯的阴极LED 灯的阳极接电源然后通过 STM32 控制该引脚的电平从而实现控制 LED 灯的亮灭。         GPIO 有很多个寄存器每一个都有特定的功能。每个寄存器为 32bit占四个字节在该外设的基地址上按照顺序排列寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以GPIOB 端口为例来说明 GPIO 都有哪些寄存器具体见表格 GPIOB 端口的寄存器地址列表。 这里我们以“ GPIO 端口置位/复位寄存器”为例教大家如何理解寄存器的说明具体见图GPIO 端口置位_复位寄存器说明。 个人总结这里的GPIOB_CRL 就相当于你家的 301但301可多了其他单元楼里也有301啊怎么区分呢那 GPIOB 就相当于区分单元楼GPIOB 是 二单元GPIOA 是 一单元等等那单元楼区分好了小区呢很多小区也有一二单元啊那 APB2 就是小区名称比如 APB2 是玉龙湖小区APB1 是石油小区这样CPU就可以不断的通过名称快速的找到你家让你家干活。 ① 名称 寄存器说明中首先列出了该寄存器中的名称“(GPIOx_BSRR)(xA…E)”这段的意思是该寄存器名为 “GPIOx_BSRR” 其中的 “x” 可以为 A-E也就是说这个寄存器说明适用于GPIOA、GPIOB至GPIOE这些GPIO端口都有这样的一个寄存器。 个人总结这里的GPIOx_BSRR相当于你家的 301这里的GPIOx_BSRR 实际上是四个字节的存储单元这四个字节共 32 位每一位都有自己的意义相当于你家 301 里边的每个人一共有32个人这32个人负责不同的工作。政府找到你家给你家每个成员赋值赋不同的值每个人干的活儿都不一样。 ② 偏移地址 偏移地址是指本寄存器相对于这个外设的基地址的偏移。本寄存器的偏移地址是 0x10从参考手册中我们可以查到 GPIOA 外设的基地址为 0x40010800我们就可以算出 GPIOA 的这个GPIOA_BSRR 寄存器的地址为0x400108000x10 同理由于 GPIOB 的外设基地址为 0x40010C00可算出 GPIOB_BSRR 寄存器的地址为0x40010C000x10。其他 GPIO 端口以此类推即可。 ③ 寄存器位表 紧接着的是本寄存器的位表表中列出它的 0-31 位的名称及权限。表上方的数字为位编号中间为位名称最下方为读写权限其中 w 表示只写r 表示只读rw 表示可读写。本寄存器中的位权限都是w所以只能写如果读本寄存器是无法保证读取到它真正内容的。而有的寄存器位只读一般是用于表示 STM32 外设的某种工作状态的由 STM32 硬件自动更改程序通过读取那些寄存器位来判断外设的工作状态。 ④ 位功能说明 位功能是寄存器说明中最重要的部分它详细介绍了寄存器每一个位的功能。例如本寄存器中有两种寄存器位分别为 BRy 及 BSy其中的 y 数值可以是 0-15这里的 0-15 表示端口的引脚号如 BR0、BS0 用于控制 GPIOx 的第 0 个引脚若 x 表示GPIOA那就是控制 GPIOA 的第0 引脚而 BR1、BS1 就是控制GPIOA第1个引脚。         其中 BRy 引脚的说明是“0不会对相应的 ODRx 位执行任何操作1对相应 ODRx 位进行复位”。这里的“复位”是将该位设置为0的意思而“置位”表示将该位设置为1说明中的 ODRx 是另一个寄存器的寄存器位我们只需要知道 ODRx 位为1的时候对应的引脚 x 输出高电平为0的时候对应的引脚输出低电平即可(感兴趣的读者可以查询该寄存器GPIOx_ODR的说明了解)。所以如果对 BR0 写入“1”的话那么 GPIOx 的第0个引脚就会输出“低电平”但是对 BR0 写入“0”的话却不会影响 ODR0 位所以引脚电平不会改变。要想该引脚输出“高电平”就需要对“BS0”位写入“1”寄存器位 BSy 与 BRy 是相反的操作。 2.2.2 C语言对寄存器的封装 个人总结该部分就是将我上边绿字说明的部分通过C语言的形式进行代码编写实现寄存器的封装看懂以下代码首先得有一些C语言的基础知识。 以上所有的关于存储器映射的内容最终都是为大家更好地理解如何用C语言控制读写外设寄存器做准备此处是本章的重点内容。 1封装总线和外设基地址 在编程上为了方便理解和记忆我们把总线基地址和外设基地址都以相应的宏定义起来总线或者外设都以他们的名字作为宏名具体见以下代码 /* 外设基地址 —— 相当于是 你所在城市的某个区比如朝阳区 */ #define PERIPH_BASE ((unsigned int)0x40000000)/* 总线基地址 —— 相当于是 你所居住区的某个小区比如和花园小区*/ #define APB1PERIPH_BASE PERIPH_BASE // 0x40000000 #define APB2PERIPH_BASE (PERIPH_BASE 0x00010000) // 0x40010000 #define AHBPERIPH_BASE (PERIPH_BASE 0x00020000) // 0x40020000/* GPIO外设基地址 —— 相当于是 你家的单元号 */ #define GPIOA_BASE (APB2PERIPH_BASE 0x0800) // 0x40010800 #define GPIOB_BASE (APB2PERIPH_BASE 0x0C00) // 0x40010C00 #define GPIOC_BASE (APB2PERIPH_BASE 0x1000) // 0x40011000 #define GPIOD_BASE (APB2PERIPH_BASE 0x1400) // 0x40011400 #define GPIOE_BASE (APB2PERIPH_BASE 0x1800) // 0x40011800 #define GPIOF_BASE (APB2PERIPH_BASE 0x1C00) // 0x40011C00 #define GPIOG_BASE (APB2PERIPH_BASE 0x2000) // 0x40012000/* 寄存器基地址 —— 相当于是 你家的门牌号以GPIOB为例 */ #define GPIOB_CRL (GPIO_BASE 0x00) // 0x40010C00 #define GPIOB_CRH (GPIO_BASE 0x04) // 0x40010C04 #define GPIOB_IDR (GPIO_BASE 0x08) // 0x40010C08 #define GPIOB_ODR (GPIO_BASE 0x0C) // 0x40010C0C #define GPIOB_BSRR (GPIO_BASE 0x10) // 0x40010C10 #define GPIOB_BRR (GPIO_BASE 0x14) // 0x40010C14 #define GPIOB_LCKR (GPIO_BASE 0x18) // 0x40010C18首先定义了“片上外设”基地址 PERIPH_BASE 接着在 PERIPH_BASE 上加入各个总线的地址偏移得到 APB1、APB2 总线的地址 APB1PERIPH_BASE、APB2PERIPH_BASE在其之上加入外设地址的偏移得到 GPIOA-G 的外设地址最后在外设地址上加入各寄存器的地址偏移得到特定寄存器的地址。一旦有了具体地址就可以用指针读写具体代码见以下  /* 控制 GPIOB 引脚 0 输出低电平(BSRR 寄存器的 BR0 置 1) */ *(unsigned int*)GPIOB_BSRR (0x01(160));/*控制 GPIOB 引脚 0 输出高电平(BSRR 寄存器的 BS0 置 1)*/ *(unsigned int*)GPIOB_BSRR 0x010;unsigned int temp; /*读取 GPIOB 端口所有引脚的电平(读 IDR 寄存器)*/ temp *(unsigned int*)GPIOB_IDR; 该代码使用(unsigned int*)把GPIOB_BSRR宏的数值强制转换成了地址然后再用“*”号做取指针操作对该地址的赋值从而实现了写寄存器的功能。同样读寄存器也是用取指针操作把寄存器中的数据取到变量里从而获取STM32外设的状态。 2封装寄存器列表 用上面的方法去定义地址还是稍显繁琐例如 GPIOA-GPIOE 都各有一组功能相同的寄存器如 GPIOA_ODR/GPIOB_ODR/GPIOC_ODR 等等它们只是地址不一样但却要为每个寄存器都定义它的地址。为了更方便地访问寄存器我们引入 C 语言中的结构体语法对寄存器进行封装具体见以下代码 typedef unsigned intuint32_t; /* 无符号32位变量 */ typedef unsigned short intuint16_t; /* 无符号16位变量 *//* GPIO寄存器列表 */ typedef struct {uint32_tCRL; /* GPIO端口配置低寄存器地址偏移:0x00 */uint32_tCRH; /* GPIO端口配置高寄存器地址偏移:0x04*/uint32_tIDR; /* GPIO数据输入寄存器址偏移:0x08*/uint32_tODR; /* GPIO数据输出寄存器地址偏移:0x0C*/uint32_tBSRR; /* GPIO位设置/清除寄存器地址偏移:0x10*/uint32_tBRR; /* GPIO端口位清除寄存器地址偏移:0x14*/uint16_tLCKR; /* GPIO端口配置锁定寄存器地址偏移:0x18*/ }GPIO_TypeDef; 这段代码用 typedef 关键字声明了名为 GPIO_TypeDef 的结构体类型结构体内有7个成员变量变量名正好对应寄存器的名字。C 语言的语法规定结构体内变量的存储空间是连续的其中32 位的变量占用 4 个字节16 位的变量占用 2 个字节具体见图 GPIO_TypeDef 结构体成员的地址偏移。 也就是说我们定义的这个 GPIO_TypeDef 假如这个结构体的首地址为 0x40010C00这也是第一个成员变量CRL的地址那么结构体中第二个成员变量CRH的地址即为0x40010C000x04 加上的这个 0x04正是代表 CRL所占用的4个字节地址的偏移量其它成员变量相对于结构体首地址的偏移在上述代码右侧注释已给。         这样的地址偏移与 STM32GPIO 外设定义的寄存器地址偏移一一对应只要给结构体设置好首地址就能把结构体内成员的地址确定下来然后就能以结构体的形式访问寄存器具体见以下代码 GPIO_TypeDef *GPIOx; //定义一个 GPIO_TypeDef 型结构体指针 GPIOx GPIOx GPIOB_BASE; //把指针地址设置为宏 GPIOB_BASE 地址 GPIOx-IDR 0xFFFF; GPIOx-ODR 0xFFFF;uint32_t temp; temp GPIOx-IDR;//读取 GPIOB_IDR 寄存器的值到变量 temp 中 这段代码先用 GPIO_TypeDef 类型定义一个结构体指针 GPIOx并让指针指向地址GPIOB_BASE(0x40010C00)使用地址确定下来然后根据 C 语言访问结构体的语法用GPIOx-ODR及GPIOx-IDR 等方式读写寄存器。         最后我们更进一步直接使用宏定义好 GPIO_TypeDef 类型的指针而且指针指向各个GPIO 端口的首地址使用时我们直接用该宏访问寄存器即可具体见以下代码 /* 使用GPIO_TypeDef把地址强制转换成指针 */ #define GPIOA ((GPIO_TypeDef*)GPIOA_BASE) #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE) #define GPIOC ((GPIO_TypeDef*)GPIOC_BASE) #define GPIOD ((GPIO_TypeDef*)GPIOD_BASE) #define GPIOE ((GPIO_TypeDef*)GPIOE_BASE) #define GPIOF ((GPIO_TypeDef*)GPIOF_BASE) #define GPIOG ((GPIO_TypeDef*)GPIOG_BASE) #define GPIOH ((GPIO_TypeDef*)GPIOH_BASE)/* 使用定义好的宏直接访问 */ /* 访问 GPIOB 端口的寄存器 */ GPIOB-BSRR 0xFFFF; //通过指针访问并修改GPIOB_BSRR寄存器 GPIOB-CRL 0xFFFF; //修改GPIOB_CRL寄存器 GPIOB-ODR 0xFFFF; //修改GPIOB_ODR寄存器uint32_t temp; temp GPIOB-IDR; //读取GPIOB_IDR寄存器的值到变量temp中/* 访问GPIOA端口的寄存器 */ GPIOA-BSRR 0xFFFF; GPIOA-CRL 0xFFFF; GPIOA-ODR 0xFFFF;uint32_t temp; temp GPIOA-IDR; //读取GPIOA_IDR寄存器的值到变量temp中 这里我们仅是以 GPIO 这个外设为例给大家讲解了 C 语言对寄存器的封装。以此类推其他外设也同样可以用这种方法来封装。好消息是这部分工作都由固件库帮我们完成了这里我们只是分析了下这个封装的过程让大家知其然也只其所以然。 2.2.3 修改寄存器的位操作方法 使用 C 语言对寄存器赋值时我们常常要求只修改该寄存器的某几位的值且其它的寄存器位不变这个时候我们就需要用到 C 语言的位操作方法了。 1把变量的某位清零 此处我们以变量a代表寄存器并假设寄存器中本来已有数值此时我们需要把变量a的某一位清零且其它位不变方法见以下代码 //定义一个变量 a 10011111b(二进制数) unsigned char a 0x9f;//对bit2清零 a ~(12);//括号中的 1 左移两位(12)得二进制数00000100b //按位取反~(12)得11111011b //假如 a 中原来的值为二进制数a 10011111b //所得的数与a作”位与”运算a (10011111b)(11111011b), //经过运算后a 的值a 10011011b //a 的 bit2 位被被零而其它位不变。 2把变量的某几个连续位清零 由于寄存器中有时会有连续几个寄存器位用于控制某个功能现假设我们需要把寄存器的某几个连续位清零且其它位不变方法见以下代码 //若把 a 中的二进制位分成 2 个一组 //即 bit0、bit1 为第 0 组bit2、bit3 为第 1 组 //bit4、bit5 为第 2 组bit6、bit7 为第 3 组 //要对第 1 组的 bit2、bit3 清零a ~(32*1); //括号中的 3 左移两位(32*1)得二进制数00001100b //按位取反~(32*1)得11110011b //假如 a 中原来的值为二进制数a 10011111b //所得的数与 a 作”位与”运算a (10011111b)(11110011b),//经过运算后a 的值 a 10010011b //a 的第1组的bit2、bit3被清零而其它位不变。//上述(~(32*1))中的(1)即为组编号;如清零第3组bit6、bit7此处应为3 //括号中的(2)为每组的位数每组有2个二进制位;若分成4个一组此处即为4 //括号中的(3)是组内所有位都为1时的值;若分成4个一组此处即为二进制数“1111b”//例如对第 2 组 bit4、bit5 清零 a ~(32*2); 3对变量的某几位进行赋值 寄存器位经过上面的清零操作后接下来就可以方便地对某几位写入所需要的数值了且其它位不变方法见以下代码这时候写入的数值一般就是需要设置寄存器的位参数。 //a 10000011b //此时对清零后的第2组bit4、bit5设置成二进制数“01b”a|(12*2); // 1 左移四位然后与 a 进行或运算//a10010011b成功设置了第2组的值其它组不变 4对变量的某位取反 某些情况下我们需要对寄存器的某个位进行取反操作即1变00变1这可以直接用如下操作  异或运算不同为 1 相同为 0其它位不变方法见以下代码 //a 10010011b //把bit6取反其它位不变a ^ (16); //a11010011b
http://www.sczhlp.com/news/160692/

相关文章:

  • 聚美优品的网站建设西双版纳网站建设开发公司
  • 重庆营销型网站建设价格网站制作可以
  • 电商网站建设与运营wordpress插件在哪
  • 大连的网站设计公司hxsp最新域名是什么
  • 博野网站建设上海市工程建设咨询监理有限公司
  • 企业网站设计代码软件网站建设公司
  • 厦门模板建站律师网站建设
  • 做网站的步骤是什么dream chaser wordpress
  • 网站访问量查询wordpress云音乐插件
  • P5709 【深基2.习6】Apples Prologue / 苹果和虫子
  • 问题表 - microsoft
  • 大型门户网站建设是什么门户网站建设 必要性
  • 站外推广营销方案策划公司宣传语
  • 网站建设教学课件网络营销推广的模式包括()
  • 云服务器搭建网站教程制作企业网站作业
  • 深圳建站公司设计深业集团企业网站建立教程
  • 邢台网站建设报价多少钱深圳方维网络
  • 四川省住房和城乡建设局网站12306网站开发成本
  • 上海 网站建设 500强教你做面食的网站
  • 易联网站建设什么是网站地址
  • 织梦网站添加视频上海高端网站建
  • 漳州 网站建设公司哪家好成功企业vi设计案例
  • wordpress网站的彻底清理自建网站与平台建站
  • 源码哥网站的模板编辑网站绑定
  • 深圳罗湖企业网站建设报价友点企业网站管理系统忘记密码
  • WordPress stockseo整站优化更能准确获得客户
  • 深入解析:[特殊字符]函数指针:C语言的动态灵魂,嵌入式的超能力(202589)
  • SolarWinds Web Help Desk远程代码执行漏洞分析
  • Aria2安装
  • 做同步网站企业营销微网站建设