• STM32F030系列实现仿位带操作


    1、闲言

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

    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
    ", _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

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

    总结

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

  • 相关阅读:
    windows 查看某个端口号被占用情况
    C# 配置文件ini操作类
    C#:如何解决WebBrowser.DocumentCompleted事件的多次调用
    什么是异或_异或运算及异或运算的作用
    UID卡、CUID卡、FUID卡的区别
    C#获取窗口大小和位置坐标 GetWindowRect用法
    C#中SetWindowPos函数详解
    C#让电脑发声,播放声音
    C#自动缩进排列代码的快捷键 c# 代码重新排版 变整齐
    安卓手机USB无法共享、上网或卡顿的解决方法
  • 原文地址:https://www.cnblogs.com/endlesscoding/p/endless-stm32f030-bit-operation.html
Copyright © 2020-2023  润新知