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)解析

STM32自学笔记的更多相关文章

  1. STM32单片机应用与全案例实践 /stm32自学笔记 第二版 pdf

    STM32单片机应用与全案例实践pdf https://pan.baidu.com/s/16WrivuLcHvLTwS__Zcwl6Q 4rj3 stm32自学笔记 第二版 pdf https://p ...

  2. 《Linux内核设计与实现》课本第四章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第四章自学笔记 进程调度 By20135203齐岳 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统.多任务操作系统使多个进程处于堵 ...

  3. 《Linux内核设计与实现》课本第三章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第三章自学笔记 进程管理 By20135203齐岳 进程 进程:处于执行期的程序.包括代码段和打开的文件.挂起的信号.内核内部数据.处理器状态一个或多个具有 ...

  4. 《Linux内核设计与实现》课本第十八章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第十八章自学笔记 By20135203齐岳 通过打印来调试 printk()是内核提供的格式化打印函数,除了和C库提供的printf()函数功能相同外还有一 ...

  5. STM32学习笔记——OLED屏

    STM32学习笔记--OLED屏 OLED屏的特点: 1.  模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2.  显示尺寸为0.96寸 3.  分辨率为128*64 4.   ...

  6. STM32学习笔记——点亮LED

    STM32学习笔记——点亮LED 本人学习STM32是直接通过操作stm32的寄存器,使用的开发板是野火ISO-V2版本: 先简单的介绍一下stm32的GPIO: stm32的GPIO有多种模式: 1 ...

  7. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  8. stm32学习笔记——外部中断的使用

    stm32学习笔记——外部中断的使用 基本概念 stm32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个.比如说,PA0,PB0 ...

  9. python自学笔记

    python自学笔记 python自学笔记 1.输出 2.输入 3.零碎 4.数据结构 4.1 list 类比于java中的数组 4.2 tuple 元祖 5.条件判断和循环 5.1 条件判断 5.2 ...

  10. STM32学习笔记(四)——串口控制LED(中断方式)

    目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...

随机推荐

  1. uni-app配置钉钉小程序步骤

    1.创建项目 2.在项目根目录配置或者新建package.json文件,配置代码 { "uni-app": { "scripts": { "mp-di ...

  2. Pytorch实战学习(七):高级CNN

    <PyTorch深度学习实践>完结合集_哔哩哔哩_bilibili Advanced CNN 一.GoogLeNet Inception Module:而为了减少代码的冗余,将由(卷积(C ...

  3. Vue 事件监听

    事件监听 v-on 使用v-on进行事件绑定监听,回调函数写在methods中.可以使用@的这种简写形式来代替v-on,当事件源无参数传递时,可省略括号. 语法如下所示: <button @:事 ...

  4. 关于github的自动化检测

    github 中的 Some checks were not successful什么意思呢?   在 GitHub 上,当您向存储库提交拉取请求时,如果存在自动化的检查(例如CI/CD)或在 pul ...

  5. Pyscript使用本地Pyodide配置方法

    背景 Pyscript工程本身很小,KB级别,引用的Pyodide工程比较大,因为包含了各种类库的wasm文件,默认引用了cdn上的文件,cdn上的文件又在外网,访问会中断导致不可用,于是需要使用本地 ...

  6. Flume实现写入es

    Flume定制elasticsearch sink源码 最近尝试通过Flume将消息写入elasticsearch,但是flume并没有对每个es版本提供支持,仅仅保留了对0.9版本支持,可能是由于e ...

  7. SQLSERVER自动备份数据库

    1. 通过操作系统的定时任务执行 创建两个文件,auto.bat和auto.sql,使用bat调用sql文件中的代码段 auto.bat内容 sqlcmd -S localhost,2433 -U s ...

  8. system verilog与C语言的接口(包含使用方法以及实例)

    资料来源 (1) sv绿皮书; (2) vcs user guide; (3) https://www.cnblogs.com/studyforever/p/5169243.html (4) syst ...

  9. python 文件 写入

    import sys import os # 打印当前文件的路径 print(__file__) # 打印当前文件所在文件夹的路径 print(os.path.dirname(__file__)) # ...

  10. WSL2 网络异常排查 [ping 不通、网络地址异常、缺少默认路由、被宿主机防火墙拦截]

    最近在使用的 wsl2 的时候突然发现 wsl2 无法正常联网,即 ping 不通外网以及宿主机的 wsl 网卡.但是将 wsl 版本设置为 1 就可以联网了. 如果你是正常使用的时候,并且自己没有手 ...