STM32F030系列实现仿位带操作

1、闲言

最近开发的时候,用到了STM32F030F4P6型号的单片机,它只有20个引脚,价格非常便宜,但是功能齐全;定时器、外部中断、串口、IIC、SPI、DMA和WWDG等等,应用尽有,非常适合用来做小设备。可是有个问题是,它是Cortex-M0内核的,不像M3,M4内核一样,可以支持位带操作(就是一位一位地操作,像80C51单片机一样),这就给程序移植或者开发带来了一点点小麻烦,因此我就利用C语言结构的位段操作,实现了个访位带操作,只是在效率可能会稍逊于真正的位带操作,但是代码上可以兼容,基本上可以应用于任何一款处理器。希望能够帮到大家。本文原文地址:http://www.cnblogs.com/endlesscoding/p/7429743.html,转载请说明出处。

2、位带操作基本知识

关于真正的位带操作,网上有不少的资料,写得也很详细,在这里我只是简单说一下我的理解。另,不理解真正的位带操作,也不影响对本文的理解,因本文跟位带操作没有任何关系,只是仿仿罢了,不能当真。如果不想了解货真价实的位带操作,此节可直接忽略。

如果不使用位带操作,我们操作一个次数据时,就要动32位(STM32是32位的),做一个不恰当的比喻,这就相当于我们坐在一辆有32节车厢的火车上,但是辆火车只有一个门,如果我们要查看这火车中乘客的信息,或者是乘客想下车,必须从那一个门进出,如下图1。

图1 只有1个车门的32节火车

而如果我们有了位带操作,就相当于,给这辆32节车厢的火车装上了32个车门,这样一来,想查看哪个乘客的信息,或都那个乘客要下车,都可以迅速地从指定的车门下车。如下图2所示。

图2 有32个车门的32节火车

有了32个门后,速度就快多了,但是硬件成本肯定要起来了,这就是为什么STM32F030系列没有位带操作的原因,就是它的成本低。

3、C言语结构体位段操作

此节主要讲述C语言结构体的基础知识,如果有C言高手,请无视此节。在C语言中,对结构体的声明,有一个位域,它可以控制,此结构体中的成员占几个位,关于它的使用,有如下代码:

 1 typedef struct _16_Bits_Struct
 2 {
 3     u16 bit0 : 1;//占一个字节
 4     u16 bit1 : 1;
 5     u16 bit2 : 1;
 6     u16 bit3 : 1;
 7     u16 bit4 : 1;
 8     u16 bit5 : 1;
 9     u16 bit6 : 1;
10     u16 bit7 : 1;
11     u16 bit8 : 1;
12     u16 bit9 : 1;
13     u16 bit10 : 1;
14     u16 bit11 : 2;//占两个字节
15     u16 bit12 : 3;//占三个字节
16 } _16_Bits_Struct;

上面的_16_Bits_Struct结构体类型共占用2个字节,即16位,但它的13个成员变量所占用的位数不全都一样,通过“:”后面的数字可决定它占几位。代码如下,操作一个此结构体类型的位。

 1     _16_Bits_Struct _16_bits;
 2     unsigned short _16bits_data;
 3     memset(&_16_bits, 0, sizeof(_16_Bits_Struct));//将其内存清0
 4
 5     _16_bits.bit2 = 1;
 6     _16_bits.bit5 = 1;
 7     _16_bits.bit8 = 5;
 8
 9     _16bits_data = *((unsigned short*)(&_16_bits));
10
11     printf("_16bits_data = %0xH\n", _16bits_data);

其输出结果为:

从结果中可以看出,在结构体,从bit0~bit12依次是从低位到高位。在上面代码的第7行,虽然给bit8写入了5,但是因为它只占一位,所以只取了5(D)=0101(B)的最低位,即为1。因此最终结果为124H,它的内存结构如下图3所示。

图3 结构体内存结构图

4、STM32F030仿位带操作

有了上面结构体位段操作的基础后,离实现仿STM32F030的位带操作就很近了。我打算做一个最简单的,实现对GPIO的某一个引脚操作,达到亮灭LED的功能。

从STM32F030的参考手册中,找到GPIO的输出寄存器ODR,看到它的基本信息如下图4所示,这个寄存器是可读可写的(RW),因此只要作我们给这个寄存器其中的一个位写入1,那么这个引脚就会输出1,写0就输出0(当然前提条件是你把它配置成输出模式,并且使能了它的时钟)。

图4 GPIO的ODR寄存器结构图

我是如何对这个寄存器一次只操作一位的呢,且看下面代码再来解释。

 1 typedef struct _16_Bits_Struct
 2 {
 3     u16 bit0 : 1;
 4     u16 bit1 : 1;
 5     u16 bit2 : 1;
 6     u16 bit3 : 1;
 7     u16 bit4 : 1;
 8     u16 bit5 : 1;
 9     u16 bit6 : 1;
10     u16 bit7 : 1;
11     u16 bit8 : 1;
12     u16 bit9 : 1;
13     u16 bit10 : 1;
14     u16 bit11 : 1;
15     u16 bit12 : 1;
16     u16 bit13 : 1;
17     u16 bit14 : 1;
18     u16 bit15 : 1;
19 } Bits_16_TypeDef;
20 #define LED_GPIO_CLK   RCC_AHBPeriph_GPIOA
21 #define LED_PORT       GPIOA
22 #define LED_PIN        GPIO_Pin_4
23 //使用结构体的位段操作, 兼容Cortex-M3的位带操作.
24 #define LED_PORT_OUT    ((Bits_16_TypeDef *)(&(LED_PORT->ODR)))
25 #define LED             (LED_PORT_OUT->bit4) 

我的硬件连接是:LED接GPIOA的4引脚上。1~19行在前面的结构体知识中已经做出了解释了,20~22只是为了代码更好移植做的一些宏定义,可不要。24行就比较关键了:先取出GPIOA->ODR的地址,然后再将它强制转化为Bits_16_TypeDef * 类型(注意,是指针类型)。转化为此类型后,ODR就有位域的特性了,因此就可以对它进行位操作。25行就是将接在PA.4的LED定义为GPIOA->ODR的第4位。

有了这样的操作后,想要我们的LED亮灭,就很容易了,代码如下。

1 LED = 0;//LED亮
2 LED = 1;//LED灭

因硬件的连接不同,效果可能是反的。看到这里,是不是觉得操作起来很简单呢。

完整的代码如下:

 1 /*------------------------------------------------------------------------------
 2                              风机监测系统(2017年8月12日12:22:38)
 3 功能描述:
 4         LED的开关功能,主要用于状态显示
 5
 6 使用资源:GPIOA随板子不用而变化
 7
 8 文件说明:无
 9 作者:Endless  邮箱:[email protected]  时间:2017年8月10日21:35:38
10 修改:无  时间:
11 ------------------------------------------------------------------------------*/
12 #include "led.h"
13 #include "stm32f0xx.h"
14
15 /*-----------------------------------------------------------------------------
16 函数功能:LED初始化
17 函数参数:无
18 函数返回:无
19 函数说明:调用此函数前,需要在LED.h修改宏定义LED引脚
20 作者:Endless
21 -----------------------------------------------------------------------------*/
22 void LED_Init(void)
23 {
24     GPIO_InitTypeDef GPIO_InitStructure;
25
26     RCC_AHBPeriphClockCmd(LED_GPIO_CLK, ENABLE);
27
28     GPIO_InitStructure.GPIO_Pin = LED_PIN;
29     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
30     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
31     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
32     GPIO_Init(LED_PORT, &GPIO_InitStructure);
33 }

led.c

 1 #ifndef __led_H
 2 #define __led_H
 3
 4 #include "stm32f0xx.h"
 5 #include "mytype.h"
 6
 7 #define LED_GPIO_CLK   RCC_AHBPeriph_GPIOA
 8 #define LED_PORT          GPIOA
 9 #define LED_PIN        GPIO_Pin_4
10
11 //使用结构体的位段操作, 兼容Cortex-M3的位带操作.
12 #define LED_PORT_OUT    ((Bits_16_TypeDef *)(&(LED_PORT->ODR)))
13 #define LED               (LED_PORT_OUT->bit4)
14
15 void LED_Init(void);
16
17 #endif

led.h

 1 #ifndef  __MYTYPE_H
 2 #define  __MYTYPE_H
 3 #include "stm32f0xx.h"
 4
 5 #ifndef BIT
 6 #define BIT(x)    (1 << (x))
 7 #endif
 8
 9 #ifndef u8
10 #define u8 uint8_t
11 #endif
12
13 #ifndef u16
14 #define u16 uint16_t
15 #endif
16
17 #ifndef u32
18 #define u32 uint32_t
19 #endif
20
21 #ifndef NULL
22 #define NULL 0
23 #endif
24
25 /*------------------------------------------------------------------------------
26                                     用户自定变量
27 功能描述:使用结构体的位段操作,可以实现位操作
28 作者:Endless     2017年8月13日18:32:37
29 修改:无     时间:
30 ------------------------------------------------------------------------------*/
31 typedef struct _16_Bits_Struct
32 {
33     u16 bit0 : 1;
34     u16 bit1 : 1;
35     u16 bit2 : 1;
36     u16 bit3 : 1;
37     u16 bit4 : 1;
38     u16 bit5 : 1;
39     u16 bit6 : 1;
40     u16 bit7 : 1;
41     u16 bit8 : 1;
42     u16 bit9 : 1;
43     u16 bit10 : 1;
44     u16 bit11 : 1;
45     u16 bit12 : 1;
46     u16 bit13 : 1;
47     u16 bit14 : 1;
48     u16 bit15 : 1;
49 } Bits_16_TypeDef;

mytype.h

如果你想进行更多的位操作,只需多定义几次就行了,很容易的。到这里就差不多结束了,希望能够帮到大家,有什么问题可以联系我,或在下面留言。

总结

做技术也很不容易,希望我们大家一起坚持下去!!

时间: 2024-10-24 19:03:00

STM32F030系列实现仿位带操作的相关文章

08_stm32位带操作

一:位带操作介绍 1.   位带操作 在学习 51 单片机的时候就使用过位操作, 通过关键字 sbit 对单片机 IO 口进行位定义. 但是 STM32 没有这样的关键字, 而是通过访问位带别名区来实现.即将每个比特位膨胀成一个 32 位字, 当访问这些字的时候就达到了访问比特的目的. 比方说 BSRR 寄存器有 32 个位, 那么可以映射到 32 个地址上, 当我们去访问这 32 个地址就达到访问 32 个比特的目的. STM32F1 中有两个区域支持位带操作, 一个是 SRAM 区的最低 1

STM32位带操作总结---浅显易懂

正在准备做毕业设计,配置LED_Config()的时候,又看到了位带操作的宏定义,我又嘀咕了,什么是位带操作,一年前在使用位带操作的时候,就查阅过好多资料,Core-M3也看过,但是对于博主这种"低能儿"来说,你不把它说的白一点,就是感觉理解的不够透彻,于是今天又一次,查阅了各种手册,也算是基本弄懂了,鉴于博主的个人特点,所以本人的介绍也会十分浅显易懂,希望能帮到各位! 首先,抛砖引玉,来两个问题: 1)为什么STM32里面会有位带操作? 2)STM32里面的位带操作是什么意思? 我也

STM32之GPIO端口位带操作

#ifndef __SYS_H #define __SYS_H #include "stm32f10x.h" //位带操作 //把“位带地址+位序号”转换别名地址宏 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) //把该地址转换成一个指针 #define MEM_ADDR(addr) *((volatil

Duanxx的STM32学习:GPIO的位带操作

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写.在 CM3中,有两个区中实现了位带.其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围.这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的"位带别名区",位带别名区把每个比特膨胀成一个 32 位的字.当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的. 关于位带操作的博客说明有很多,这里主要将代码贴出来,并做详细的注释 /** ************

位带操作

1.位带区: 支持位带操作的地址区 2.位带别名:对别名地址的访问最终作用在位带区的访问(中途有地址映射) 3. 4. 5. 对于片上外设,映射关系参照上图关系修改即可. 6.举例: 建立一个把“位带地址+位序号”换成别名地址的宏,再建立一个把别名地址转换成指针类型的宏. 使用位带功能时,要访问的变量必须用volatile 来定义.(指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.)

第13章 GPIO—位带操作

第13章     GPIO-位带操作 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege 本章参考资料:<STM32F4xx 中文参考手册>存储器和总线构架章节.GPIO章节,<Cortex?-M4内核编程手册>2.2.5 Bit-banding.学习本章时,配套这些参考资料学习效果会更佳. 13.1 位带简介 位操作就是可以单独的对一个比特位读和写,这个在51单片机

玩转X-CTR100 | 位带操作-GPIO

更多塔克创新资讯欢迎登陆[塔克社区www.xtark.cn ][塔克博客www.cnblogs.com/xtark/ ] STM32F4位带概念,及位带的GPIO操作实践应用. 原理介绍 51单片机相信各位都用过,假设P1.1的IO口上挂了一个LED,那么你单独对LED的操作就是P1.1 = 0或P1.1 = 1,注意,是你可以单独的对P1端的第一个IO口进行操作,然而STM32是不允许这样做的,那么为了像51单片机一样能够单独的对某个端的某一个IO单独操作,就引入了位带操作这样的概念,简单说就

STM32中的位带(bit-band)操作

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写.在 CM3 中,有两个区中实现了位带.其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB范围.这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的"位带别名区",位带别名区把每个比特膨胀成一个 32 位的字.当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的.       位带操作的概念其实 30 年前就有了,那还是8051 单片机开创的先河,如今,CM3 将

第13章 GPIO-位带操作—零死角玩转STM32-F429系列

第13章 ????GPIO-位带操作 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege ? 本章参考资料:<STM32F4xx 中文参考手册>存储器和总线构架章节.GPIO章节,<Cortex?-M4内核编程手册>2.2.5 Bit-banding.学习本章时,配套这些参考资料学习效果会更佳. 13.1 位带简介 位操作就是可以单独的对一个比特位读和写,这个在51单