历史上的今天
返回首页

历史上的今天

今天是:2025年07月10日(星期四)

正在发生

2018年07月10日 | STM32F030系列实现仿位带操作

2018-07-10 来源:eefocus

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  邮箱:endless@139.com  时间: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

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


总结


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


推荐阅读

史海拾趣

GAPTEC Electronic GmbH & Co. KG公司的发展小趣事

背景:在闪存市场取得成功后,Galaxy Microelectronics开始探索多元化发展道路。

发展:公司决定进入DRAM市场,并投入大量资源进行技术研发。经过几年的努力,Galaxy Microelectronics成功推出了多款高性能DRAM产品,进一步丰富了其产品线。此外,公司还开始涉足SSD固态硬盘领域,推出了多款具有竞争力的产品,进一步巩固了其在存储市场的地位。

广东爱晟电子(exsense)公司的发展小趣事

广东爱晟电子自成立以来,一直致力于NTC热敏电阻技术的研发和创新。在2010年代初,公司面临技术瓶颈,市场竞争激烈。然而,爱晟电子团队不畏艰难,通过深入研究,成功研发出具有更高精度和稳定性的NTC热敏电阻。这一技术突破迅速获得了市场的认可,公司销售额逐年攀升,奠定了在电子行业的领先地位。

Conexcon Group公司的发展小趣事

在电子行业的快速发展中,Conexcon Group以其前瞻性的技术视野,成功研发出一款新型智能芯片,这款芯片不仅性能卓越,而且功耗极低,迅速赢得了市场的青睐。公司凭借这一技术突破,逐步扩大了在国内外市场的份额,成为了行业内技术创新的领军者。随着产品线的不断丰富,Conexcon Group逐渐形成了从芯片设计到整机制造的完整产业链,进一步巩固了其在电子行业的领先地位。

ABL Heatsink公司的发展小趣事

ABL Heatsink公司在发展过程中,积极寻求与其他企业的合作。通过与芯片制造商、电子设备生产商等建立紧密的合作关系,ABL Heatsink成功将自己的散热器产品集成到各种高端电子设备中。这种合作模式不仅提升了公司的知名度,也带来了稳定的订单和收入。

川土(Chipanalog)公司的发展小趣事

经过数月的紧张研发与测试,川土微电子成功推出了其首款自研产品——一款高性能的隔离器芯片。这款芯片的问世不仅填补了国内市场的空白,也标志着川土微电子在模拟芯片领域的初步成功。随后,公司开始逐步扩大产品线,涵盖了接口、高性能模拟等多个系列。

GWM Associates公司的发展小趣事
如高级音响系统、录音棚设备、专业音频制作设备等,用于提高音频信号的清晰度和保真度。

问答坊 | AI 解惑

为什么串口发送不能停止?

个位大侠,我用89s52利用其串口发送一个字符串,为什么不能够停止?很简单的程序,就是不知道问题在那里,盼望得到指导,程序如下: #include                       ...…

查看全部问答>

谢谢!求基于DSP的文字图像处理程序

请教一下,基于DSP的文字图像处理的程序代码,汇编的,C的都行,简单的,只要能够识别几个字就行,谢谢,…

查看全部问答>

基于ARM和Linux的嵌入式系统的研究以及打印模块的实现(南理工硕士论文)

本帖最后由 paulhyde 于 2014-9-15 09:34 编辑 随着计算机技术的飞速发展,嵌入式系统将在人们的生产生活中发挥越来越重要的作用。一方面,ARM技术已经在当今的嵌入式微处理器领域中占据了领先地位,另一方面,结构清晰、源码开放的Linux已经发展 ...…

查看全部问答>

谁愿意当我的师傅,真的有点迷茫啊

谁愿意当我的师傅啊,刚毕业进入一个公司,要求用CPLD做一个设计,但因为小公司嘛,没有培训,自己学的都是皮毛,有时候搞了很久发现走弯路了,振希望有个人能指导下我,公司也没这方面的人,以前都是用单片机,现在是DSP+CPLD,CPLD很多问题不明白 ...…

查看全部问答>

用I2C总线往G-sensor的寄存器中写数据怎么写不进去?盼高手指点!

硬件没有问题,我检查过是好的。 读寄存器的程序是没有问题的,能够读出来数据,但是往寄存器里写数据却写不进不知道是为什么。 我用串口把每一步的I2c状态都读出来,和单片机手册上的要求是一样的,而且写程序的时候也是按照Gsensor要求的时序写 ...…

查看全部问答>

聘请兼职电路板、ic设计工程师和无线电遥感工程师及玩具模具设计师

电路板、ic设计工程师要求:1、电子电路相关专业大专以上;2、从事电路板、ic等电子器件的设计、开发或制作3年以上工作经验;3、非常熟悉光电转换放大电路、频率计算器件,单片机系统、嵌入式系统;4、本工作性质:主要负责设计开发一定功能的电路 ...…

查看全部问答>

求助关于2812串口发送数据的问题!

我最近开始学习DSP2812,手上有合重达的2812开发板,在测试串口例程时,按照用户指南上的步骤做,但PC机不能接收到数据,我就自己弄了个用DSP发送数据的程序,就想看下PC能接收到数据不? 现在的问题是用在PC上用串口提示助手能够接收到数据,但是 ...…

查看全部问答>

求简易逻辑分析仪电路

谁有简易逻辑分析仪电路啊,急求啊…

查看全部问答>