• STM32自学笔记


    1位带操作

    第一种位带操作

    #define BITBAND_REG(Reg,Bit) (*((uint32_t volatile*)(0x42000000u + (((uint32_t)&(Reg) - (uint32_t)0x40000000u)<<5)+(((uint32_t)(Bit))<<2))))
    #define D0	BITBAND_REG(GPIOF->ODR,9)
    #define D1	BITBAND_REG(GPIOF->ODR,10)
    #define D2	BITBAND_REG(GPIOE->ODR,13)
    #define D3	BITBAND_REG(GPIOE->ODR,14)
    

    参考文档

    第二种位带操作

    建立一个文件(.h)文件,输入如下代码,然后就可以通过位带操作IO口了,注意包含《"stm32f4xx.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))
    
    
    //IO口地址映射
    #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 
     
    //IO口操作,只对单一的IO口!
    //确保n的值小于16!
    #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
    #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
    
    #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
    #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
    
    #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
    #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
    
    #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
    #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
    
    #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
    #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
    
    #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
    #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
    
    #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
    #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
    
    #define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
    #define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入
    
    #define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
    #define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入
    

    写完之后,我们就可以通过位带来控制stm32的IO口,比如

    #define LED0 PEout(9)
    #define LED1 PEout(10)
    

    但是在编写这个过程中遇到了一个问题,报错内容为cannot take the address of an rvalue of type 'int'

    主要是输入错误,正确输入如下:

    #define BITBAND(addr,bitnum) ((addr & 0xF0000000)+0x2000000+((addr	&	0xFFFFF)<<5)+(bitnum<<2))
    

    错误输入如下

    #define BITBAND(addr,bitnum) ((addr & 0xF0000000)+0x2000000+((addr +	&	0xFFFFF)<<5)+(bitnum<<2))
    

    仔细对比,可参考正点原子资料了解详情:

    正点原子资料下载

    2、在STM32编程遇到的一些关键字

    1、 #pragma

    在编程过程中偶尔会遇见 如下所示的代码程序:

    #if defined(__SUPPORT_SNAN__) && defined(_WANT_SNAN)
    #pragma import(__use_snan)
    #endif
    

    通过查找可以发现其位于“stdio.h”文件中,应该属于C语言内容的一部分;通过查阅资料可得知,

    pragma属于预处理命令,其解释如下

    #pragma命令的作用是使编译程序发生器向编译程序发出各种命令。

    #pragma命令的一般形式如下

    #pragma	名字
    

    这里“名字”就是调用#pragma的名字

    听完解释貌似还是好迷一样,大概意思就是用户通过#pragma这个预处理命令告诉编译器如何处理数据,一般根据参数来设置,有如下一些参数:

    参数名 功能
    message 它能够在编译信息输出窗口输出响应的信息,这对于源代码信息的控制是非常重要的。使用方法为:
    #ifdef _x86
    #pragma message("_x86 macro activated!")
    #endif
    当我们定义了_x86这个宏后,应用程序在编译时就会在编译输出窗口显示_x86 macro activated!
    code_seg 其使用格式为#pragma code_seg(["section-name"[,"section_class"]])
    它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候可以用到它。
    once 只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,注意,这条指令在VC6中已经有了,考虑兼容性并没有太多的使用它
    hdrstop 表示编译头文件到此为止,后面的头文件不进行预编译。BCB可以编译头文件以加快链接的速度,但是如果所有头文件都进行预编译又可能占用太多的磁盘空间。所有使用这个选项排除一些头文件
    startup 有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译,可以使用
    #pragma startup指定编译优先级,如果使用#pragma package(smart_init),BCB就会根据优先级先后编译。
    resource #pragma resource "**.dfm"表示把.dfm中的资源加入工程.dfm中包含窗体外观的定义
    warning #pragma warning(disable:4507 34;once:4385;error:164),等价于
    #pragma warning(disable:4507 34)//不显示4507和34号警告信息
    #pragma warning(once : 4385) //4385号警告信息仅报告一次
    #pragma warning(error:164) //把164号警告信息作为一次错误。
    comment #pragma comment(...)
    该指令将一个注释记录放入一个对象文件或可执行文件中,常用的lib关键字,可以帮助我们连入一个库文件

    参考博客

    总之#pragma指令就是将一些命令按照自己需要告诉编译器,那可以回到如下指令

    #if defined(__SUPPORT_SNAN__) && defined(_WANT_SNAN)
    #pragma import(__use_snan)
    #endif
    

    这些是C语言stdio.h文件中包含的声明,但是没有打开内容其内容;所以也不清楚这些东西是什么,我们转而描述对这些内容的疑问的来源吧

    #if 1
    #pragma import(__use_no_semihosting)             
    //标准库需要的支持函数                 
    struct __FILE 
    { 
    	int handle; 
    }; 
    
    FILE __stdout;       
    //定义_sys_exit()以避免使用半主机模式    
    void _sys_exit(int x) 
    { 
    	x = x; 
    } 
    //重定义fputc函数 
    int fputc(int ch, FILE *f)
    { 	
    	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    	USART1->DR = (u8) ch;      
    	return ch;
    }
    #endif
    

    在正点原子提供的资料的串口程序中有这么一段,我们在学C语言时我们知道可以使用现成printf();函数,但是在STM32中并未有用于底层printf();函数,需要我们重新写,因此这里就对这些函数重新定义了。

    #pragma import(__use_no_semihosting)
    

    对于上面那个语句的含义在标准库函数的默认输出设备是显示器,要实现在串口或者LCD输出,必须重新定义标准库函数里的输入与输出设备相关的函数。还有一点就是因为printf()之类的函数,使用了半主机模式。使用标准库会导致程序无法运行,可以通过下面两种方法解决。

    方法一:使用微库

    因为使用微库,不会使用半主机模式,如果使用的是MDK,可以在工程属性的”Target“—>"Code Generation"中勾选”Use MicroLIB“这样处理后就可以使用printf();sprintf()函数了。

    方法2:仍然使用标准库

    可以继续使用标准库,不过要在主程序中添加如下代码:

    #pragma import(__use_no_semihosting)  // 确保没有从 C 库链接使用半主机的函数
    _sys_exit(int  x) //定义 _sys_exit() 以避免使用半主机模式
    {
    x = x;
    }
    struct __FILE  // 标准库需要的支持函数
    {
    int handle;
    };
    /* FILE is typedef ’ d in stdio.h. */
    FILE __stdout;
    

    为确保没有从C库链接使用半主机函数,因为不使用半主机,标准C库stdio.h中有些使用半主机函数必须重新写。在独立应用程序中,不太可能支持半主机操作。因此,必须确保应用程序中没有链接C库半主机函数。为确保没有从C库连接使用半主机的函数,必须导入符号_use_no_semihosting;可在工程任何C或者汇编语言源文件中执行此操作,操作过程如下:

    //在C语言程序中,使用#pragma指令
    #pragma import(__use_no_semihosting)
    //在汇编语言中,使用IMPORT指令
    IMPORT __use_no_semihosting
    

    至此就解释了为啥要重写printf()函数,并且#pragma import(__use_no_semihosting)是什么意思;参考资料如下所述,如果失效,可直接搜索如下关键字:

    半主机

    #pragma import(__use_no_snmihosting)关于串口输入输出的重定义

    STM32 串口 #pragma import(__use_no_semihosting)解析

  • 相关阅读:
    自动登录网站
    爬取梨视频
    爬虫介绍,request模块和代理ip
    数据结构与算法
    CMDB的总结
    自动化运维模块
    linux命令补充
    centos7的目录结构,文件系统常用的命令,vim编辑器
    linux配置网卡文件,xshell链接服务,快照,克隆,修改主机名
    flask的请求扩展,错误处理,标签和过滤器,中间件以及cbv的写法
  • 原文地址:https://www.cnblogs.com/liyingji/p/14215347.html
Copyright © 2020-2023  润新知