
<p>一、带位操作<p>位带操作就是将 位带区 中的每一位(bit)膨胀成位带别名区中的一 个 32 位的字 ,通过访问位 带别名区中的字 就实现了访问位带区中位的目的.可以使用指针来访问位带别名区的地址,从而实现访问 位带区内位的 目 的。<p><br/><p>1.1、什么是带位操作<p>51单片机通过关键字sbit对单片机IO口进行位定义的过程即带位操作。<p><br/><p>1.2、STM32如何实现带位操作<p>每个比特为膨胀成一个32位字,当访问这些字的时候就达到访问比特的目的。<p><br/><p>举例:BSRR 寄存器有 32 个位,那么可以映射到 32 个地址上,当我们去访问这 32 个地址就达到访问 32 个比特的目的。<p><br/><p>STM32F1 中有两个区域支持位带操作,一个是 SRAM 区的最低 1MB 范围,一个是片内外设区的最低 1MB 范围(APB1、APB2、AHB 外设)。<p><br/><p>SRAM 的最低 1MB 区域,地址范围是 0X2000 0000-0X200F FFFF。<p><br/><p>片内外设最低 1MB 区域,地址范围是 0X4000 0000-0X400F FFFF,在这个地址范 围内包括了 APB1、APB2、AHB 总线上所有的外设寄存器。<p><img alt="" height="39" src="https://6.eewimg.cn/news/uploadfile/2021/1108/1636332868749916.png" width="280"/><p> <p><img alt="" height="240" src="https://6.eewimg.cn/news/uploadfile/2021/1108/20211108085509574.png" width="547"/><p>SRAM 区中还有 32MB 空间,其地址范围是 0X2200 0000-0X23FF FFFF,它 是 SRAM 的 1MB 位带区膨胀后的位带别名区.。<p><br/><p>要实现位操作即将每一位膨胀成一个 32 位的字,因此 SRAM 的 1MB 位带区就膨胀为 32MB 的位带别名区,通过访问位带别名区就可以实现访问位带中每一位的目的。<p><br/><p>片内外设区的 32MB 地址范围是 0X4200 0000-0X43FF FFFF。<p><br/><p>二、位带区与位带别名区地址别名转换<p>位带区的一位(bit)会在未带别名区膨胀成4字节(Byte)32位的字。<p><br/><p>2.1、外设位带别名区地址<p>外设未带别名区地址 = 0x4200 0000 + (A-0x40000000)*8*4+n*4<p>0x04200 0000:外设位带别名区的起始地址。<p>A:片上外设位带区的某个比特,记它所在字节的地址为 A。<p>0x40000000:外设未带区起始地址。<p>(A-0x40000000)表示该比特前面有多少个字节<p>*8:一字节(Byte)=8位(bit)<p>*4:1位膨胀后是4字节。<p>n:位序号为 n,n 值的范围是 0-7。<p>n*4:每一位膨胀后是四个字节。<p><br/><p>2.2、SARM位带别名区地址<p>AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4 <p><br/><p>同上<p><br/><p>2.3、整合两个公式<p>#define BITBAND(addr,bitnum) ((addr & 0xF0000000)+0x200000000 +((addr & 0x000FFFFF <<5)+(bitnum<<2))<p>与&& and<span style="white-space:pre"> </span> <span style="white-space:pre"> </span> a && b a and b<span style="white-space:pre"> </span> <p> 或|| or a || b a or b<p> 非! !1=0 <p> 按位与(&)<span style="white-space:pre"> </span> a & b a and b <p> 按位或(|) a | b a or b<p> <p>addr:位带地址。<p>bitnum:位序号。<p>addr & 0xF0000000:区分操作的是 SRAM 还是外设。<p>外设addr & 0xF0000000结果:0x4000 0000 0X42000000 是外设别名区的起始地址<p> <p>SARMaddr & 0xF0000000结果:0x2000 0000 0X22000000 是 SRAM 别名区的起始地址。<p> <p>addr & 0x000FFFFF 屏蔽了高三位 ,相当于减去 0X20000000 或者 0X40000000<p>屏蔽高三位是因为 SRAM 和外设的位带区最高地址是 0X200F FFFF 和 0X400F FFFF, SRAM 或者外设位带区上任意地址减去其对应的起始地址,总是低 5 位有效 ,所以这里屏蔽高 3 位就相当于减去了 0X20000000 或者 0X40000000。<<5 相当于*8*4, <<2 相当于*4<p><br/><p><span style="white-space:pre"> </span>// 把addr 地址强制转换为unsigned long类型指针<p><span style="white-space:pre"> </span>#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))<p><span style="white-space:pre"> </span>//把位带别名区内地址转换为指针,获取地址的数据<p><span style="white-space:pre"> </span>#define BTT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitum))<p>Volatile关键字 提醒编译器它后面所定义的 变量随时都有可能改变 ,因此编译后的程序每次需要存储或读取这个变量的时候,都会 直接从变量地址中读取数据。如果没有 volatile 关键字,则编译器可能优化读 取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话, 将出现不一致的现象.<p> <p>被volatile关键字修饰的变量,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。<p> <p>0x4000 0000-0x4000 77FF: APB1 总线外设。<p>0x4000 7800-0x4000 FFFF: 预留。<p>0x4001 0000-0x4001 3FFF: APB2 总线外设。<p>0x4001 4000-0x4001 7FFF: 预留。<p>0x4001 8000-0x4002 33FF: AHB 总线外设。<p>0x4002 4400-0x5FFF FFFF: 预留。<p> <p>三、GPIO代位操作<p>led.c<p>#include "led.h"<p>#include "system.h"<p>#include "stm32f10x_gpio.h"<p>/*<p>*@File Name:文件名 @Function <p>*@Author:作者 <p>*@Version:版本 <p>*@Date:日期 <p>*@Description:描述<p>*@brief:简介,简单介绍函数作用<p>*@param:介绍函数参数<p>*@return:函数返回类型说明<p>*@exception NSException: 可能抛出的异常.<p>*@property :属性介绍<p>*/<p>void LED_Init(void)<p>{<p><span style="white-space:pre"> </span>/*<p><span style="white-space:pre"> </span>typedef struct<p>{<p> uint16_t GPIO_Pin; 引脚定义 <p> GPIOSpeed_TypeDef GPIO_Speed; 速度定义<p> GPIOMode_TypeDef GPIO_Mode; 输出模式模式<p>}GPIO_InitTypeDef;<p><span style="white-space:pre"> </span><p>*/<p><span style="white-space:pre"> </span>GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量<p> <p><span style="white-space:pre"> </span><p><span style="white-space:pre"> </span>RCC_APB2PeriphClockCmd(LED1_PORT_RCC|LED2_PORT_RCC,ENABLE);<p><span style="white-space:pre"> </span><p><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin=LED1_PIN; //选择你要设置的IO口<p><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;<span style="white-space:pre"> </span> //设置推挽输出模式<p><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;<span style="white-space:pre"> </span> //设置传输速率<p><span style="white-space:pre"> </span>GPIO_Init(LED1_PORT,&GPIO_InitStructure); <span style="white-space:pre"> </span> /* 初始化GPIO */<p><span style="white-space:pre"> </span>GPIO_SetBits(LED1_PORT,LED1_PIN); //将LED端口拉高,熄灭所有LED<p><span style="white-space:pre"> </span><p><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin=LED2_PIN; //选择你要设置的IO口<p><span style="white-space:pre"> </span>GPIO_Init(LED2_PORT,&GPIO_InitStructure); <span style="white-space:pre"> </span> /* 初始化GPIO */<p><span style="white-space:pre"> </span>GPIO_SetBits(LED2_PORT,LED2_PIN); //将LED端口拉高,熄灭所有LED<p>}<p><br/><p>led.h<p>#ifndef _led_H<p>#define _led_H<p> <p>#include "system.h"<p> <p>/* LED时钟端口、引脚定义 */<p>#define LED1_PORT <span style="white-space:pre"> </span>GPIOB <p>#define LED1_PIN <span style="white-space:pre"> </span>GPIO_Pin_5<p>#define LED1_PORT_RCC<span style="white-space:pre"> </span>RCC_APB2Periph_GPIOB<p> <p>#define LED2_PORT <span style="white-space:pre"> </span>GPIOE <p>#define LED2_PIN <span style="white-space:pre"> </span>GPIO_Pin_5<p>#define LED2_PORT_RCC<span style="white-space:pre"> </span>RCC_APB2Periph_GPIOE<p><br/><p>#define LED1 PBout(5) <span style="white-space:pre"> </span><p>#define LED2 PEout(5) <span style="white-space:pre"> </span><p> <p>void LED_Init(void);<p><br/><p>#endif<p><br/><p>main.c<p>#include "system.h"<p>#include "led.h"<p><br/><p>/*<p>* 函 数 名 : delay<p>* 函数功能<span style="white-space:pre"> </span> : <a href="https://www.eeworld.com.cn/zhuanti/LaDCuT" style="color:#4595e6;" target="_blank">延时函数</a>,通过while循环占用CPU,达到延时功能<p>* 输 入 : i<p>* 输 出 : 无<p>*/<p>void delay(u32 i)<p>{<p><span style="white-space:pre"> </span>while(i--);<p>}<p> <p>/*<p>* 函 数 名 : main<p>* 函数功能<span style="white-space:pre"> </span> : 主函数<p>* 输 入 : 无<p>* 输 出 : 无<p>*/<p>int main()<p>{<p><span style="white-space:pre"> </span>LED_Init();<p><span style="white-space:pre"> </span>while(1)<p><span style="white-space:pre"> </span>{<p><span style="white-space:pre"> </span>LED1=!LED1;<p><span style="white-space:pre"> </span>delay(6000000);<p><span style="white-space:pre"> </span>}<p>}<p><br/><p>system.h<p>#ifndef _system_H<p>#define _system_H<p> <p>#include "stm32f10x.h"<p><br/><p>//位带操作,实现51类似的GPIO控制功能<p>//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).<p>//IO口操作宏定义<p>#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) <p>#define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) <p>#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) <p>//IO口地址映射<p>#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C <p>#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C <p>#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C <p>#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C <p>#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C <p>#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C <p>#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C <p> <p>#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 <p>#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 <p>#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 <p>#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 <p>#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 <p>#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 <p>#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 <p> <p>//IO口操作,只对单一的IO口!<p>//确保n的值小于16!<p>#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 <p>#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 <p> <p>#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 <p>#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 <p> <p>#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 <p>#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 <p> <p>#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 <p>#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 <p> <p>#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 <p>#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入<p> <p>#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 <p>#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入<p> <p>#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 <p>#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入<p> <p>#endif<p><br/><p>system.c<p>#include "system.h"
<p>一、C语言之预处理<p>预处理有三种:宏定义、条件编译、文件包含。<p><br/><p>1.1、宏定义<p>#define EXT extern // 用EXT代替extern关键字<p><br/><p>1.2、文件包含<p>#include “stm32f10x.h”<p><br/><p>1.3、条件编译<p>防止同一个头文件被不同的文件多次包含,编译时所有文件编译成一个可执行文件有大量命名冲突。<p><br/><p>#ifndef <标识符> //if not defined<p>#define <标识符><p>代码段落<p>#endif<p> <p>#ifndef _public_h <p>#define _public_h<p>#include "public.h"<p>#endif<p>#空指令,无任何效果<p>#include包含一个源代码文件<p>#define定义宏<p>#undef取消已定义的宏<p>#if如果给定条件为真,则编译下面代码<p>#ifdef如果宏已经定义,则编译下面代码<p>#ifndef如果宏没有定义,则编译下面代码<p>#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码<p>#endif结束一个#if……#else条件编译块<p>#error停止编译并显示错误信息<p><br/><p>1.4、C语言中的宏机制<p>c程序编译分过程分为三个步骤:(1)预处理;(2)编译;(3)链接。在预处理阶段,编译器中的预处理器会将定义的宏展开,即 原封不动的替换宏的定义,只是在处理文本。<p><br/><p>二、typedef enum{};与enum{};<p>typedef 为C语言的关键字,释义:为各种数据类型定义一个新名字(别名)。<p><br/><p>2.1、枚举类型的声明<p>enum [枚举名] {枚举元素列表};<p><br/><p>typedef enum [枚举名] {枚举元素列表};<p><br/><p>typedef enum{FALSE=0,TURE=1}bool; // bool为此枚举类型的变量,它的值只能是由FALSE与TURE赋予。<p>typedef enum{FALSE=0,TURE=!FALSE}bool;// FALSE与TURE不能是小写,小写是C语言的保留字。<p><br/><p>三、结构体与联合体<p>结构体和联合体用于描述事物的属性,如一只鸟的信息,可能包括它的品种,体重,颜色,年龄等。<p><br/><p>用户根据自己的需求构造的数据类型,但必须“先定义,后使用”。<p><br/><p>用户必须先构造一个结构体类型,然后才能使用这个结构体类型来定义变量或数组。<p>struct 结构体名称{}结构体别名;<p><br/><p>/*定义一个结构体sbit,取别名Bit,表示8个位域结构<p>unsigned bit0为位段名 1为位的个数*/<p>typedef struct sbit<p>{ <p>unsigned bit0 : 1; <p>unsigned bit1 : 1; <p>unsigned bit2 : 1; <p>unsigned bit3 : 1; <p>unsigned bit4 : 1; <p>unsigned bit5 : 1; <p>unsigned bit6 : 1; <p>unsigned bit7 : 1;<p>}Bit; <p><br/><p>四、C语言数据类型<p><img alt="" height="362" src="https://6.eewimg.cn/news/uploadfile/2021/1108/20211108085637956.jpg" width="463"/><p>unsigned无符号整型, 例如 int 型的范围:-2^31 ~ 2^31 - 1,而unsigned int的范围:0 ~ 2^32。看起来unsigned 是个不错的类型,尤其是用在自增或者没有负数的情况。但是在实际使用中会出现一些意外的情况。<p><br/><p>signed在默认情况下声明的整型变量都是有符号的类型(char有点特别),如果需声明无符号类型的话就需要在类型前加上unsigned。无符号版本和有符号版本的区别就是无符号类型能保存2倍于有符号类型的正整数数据。<p><br/><p>五、stm32中的数据类型<p>typedef unsigned short int uint16_t<p><br/><p>参考:https://www.cnblogs.com/wangh0802PositiveANDupward/archive/2013/01/01/2841697.html<p><br/><p>在STM32编程中,常用的数据类型有:char(字符型),u8,u16 ,u32,但是在一些计算中,涉及到负数,小数,因此要用到:int float doulbe 型。<p>其中u8——1个字节,无符号型(不能表达负数,如果用来当作负数的话,就出错了);<p> u16 ——2个字节,无符号型(参看前边STM32f10x.h中的定义);<p> u32——4个字节,无符号型;<p> int——4个字节,有符号型,可以表达负整数;<p> float ——4个字节,有符号型,可以表达负数/小数;<p> double——8个字节,有符号弄,可以表达负数/小数;<p><br/><p>六、stm32时钟<p><img alt="" height="911" src="https://6.eewimg.cn/news/uploadfile/2021/1108/20211108085638945.jpg" width="861"/><p>HCLK:送给AHB总线、内核、内存和DMA使用的HCLK时钟。<p>HSI:高速内部时钟<p>FLITFCLK:到Flash编程接口<p>LSE:低速外部时钟<p>HSE:高速外部时钟<p>FCLK:直接送给Cortex的自由运行时钟(free running clock)FCLK<p> 、<p>GPIO_Mode_IN = 0x00 // GPIO Input Mode 输入模式<p>GPIO_Mode_OUT = 0x01 // GPIO Out Mode 输出模式<p>GPIO_Mode_AF = 0x02 // GPIO Alternate Function Mode 复用模式<p>GPIO_Mode_AN= 0x03 // GPIO Analog In/Out Mode 模拟输入/输出模式<p><br/><p>进制转换表<p><img alt="" height="362" src="https://6.eewimg.cn/news/uploadfile/2021/1108/20211108085637956.jpg" width="463"/>
讯享网

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/205117.html