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语言中,对结构体的声明,有一个位域,它可以控制,此结构体中的成员占几个位,关于它的使用,有如下代码:

 typedef struct _16_Bits_Struct
{
u16 bit0 : ;//占一个字节
u16 bit1 : ;
u16 bit2 : ;
u16 bit3 : ;
u16 bit4 : ;
u16 bit5 : ;
u16 bit6 : ;
u16 bit7 : ;
u16 bit8 : ;
u16 bit9 : ;
u16 bit10 : ;
u16 bit11 : ;//占两个字节
u16 bit12 : ;//占三个字节
} _16_Bits_Struct;

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

     _16_Bits_Struct _16_bits;
unsigned short _16bits_data;
memset(&_16_bits, , sizeof(_16_Bits_Struct));//将其内存清0 _16_bits.bit2 = ;
_16_bits.bit5 = ;
_16_bits.bit8 = ; _16bits_data = *((unsigned short*)(&_16_bits)); 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寄存器结构图

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

 typedef struct _16_Bits_Struct
{
u16 bit0 : ;
u16 bit1 : ;
u16 bit2 : ;
u16 bit3 : ;
u16 bit4 : ;
u16 bit5 : ;
u16 bit6 : ;
u16 bit7 : ;
u16 bit8 : ;
u16 bit9 : ;
u16 bit10 : ;
u16 bit11 : ;
u16 bit12 : ;
u16 bit13 : ;
u16 bit14 : ;
u16 bit15 : ;
} Bits_16_TypeDef;
#define LED_GPIO_CLK RCC_AHBPeriph_GPIOA
#define LED_PORT GPIOA
#define LED_PIN GPIO_Pin_4
//使用结构体的位段操作, 兼容Cortex-M3的位带操作.
#define LED_PORT_OUT ((Bits_16_TypeDef *)(&(LED_PORT->ODR)))
#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亮灭,就很容易了,代码如下。

 LED = ;//LED亮
LED = ;//LED灭

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

完整的代码如下:

 /*------------------------------------------------------------------------------
风机监测系统(2017年8月12日12:22:38)
功能描述:
LED的开关功能,主要用于状态显示 使用资源:GPIOA随板子不用而变化 文件说明:无
作者:Endless 邮箱:endless@139.com 时间:2017年8月10日21:35:38
修改:无 时间:
------------------------------------------------------------------------------*/
#include "led.h"
#include "stm32f0xx.h" /*-----------------------------------------------------------------------------
函数功能:LED初始化
函数参数:无
函数返回:无
函数说明:调用此函数前,需要在LED.h修改宏定义LED引脚
作者:Endless
-----------------------------------------------------------------------------*/
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(LED_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = LED_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(LED_PORT, &GPIO_InitStructure);
}

led.c

 #ifndef __led_H
#define __led_H #include "stm32f0xx.h"
#include "mytype.h" #define LED_GPIO_CLK RCC_AHBPeriph_GPIOA
#define LED_PORT GPIOA
#define LED_PIN GPIO_Pin_4 //使用结构体的位段操作, 兼容Cortex-M3的位带操作.
#define LED_PORT_OUT ((Bits_16_TypeDef *)(&(LED_PORT->ODR)))
#define LED (LED_PORT_OUT->bit4) void LED_Init(void); #endif

led.h

 #ifndef  __MYTYPE_H
#define __MYTYPE_H
#include "stm32f0xx.h" #ifndef BIT
#define BIT(x) (1 << (x))
#endif #ifndef u8
#define u8 uint8_t
#endif #ifndef u16
#define u16 uint16_t
#endif #ifndef u32
#define u32 uint32_t
#endif #ifndef NULL
#define NULL 0
#endif /*------------------------------------------------------------------------------
用户自定变量
功能描述:使用结构体的位段操作,可以实现位操作
作者:Endless 2017年8月13日18:32:37
修改:无 时间:
------------------------------------------------------------------------------*/
typedef struct _16_Bits_Struct
{
u16 bit0 : ;
u16 bit1 : ;
u16 bit2 : ;
u16 bit3 : ;
u16 bit4 : ;
u16 bit5 : ;
u16 bit6 : ;
u16 bit7 : ;
u16 bit8 : ;
u16 bit9 : ;
u16 bit10 : ;
u16 bit11 : ;
u16 bit12 : ;
u16 bit13 : ;
u16 bit14 : ;
u16 bit15 : ;
} Bits_16_TypeDef;

mytype.h

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

总结

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

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

  1. (stm32学习总结)—GPIO位带操作

    本章参考资料:<STM32F10X-中文参考手册>存储器和总线构架章节.GPIO 章节,<CM3 权威指南 CnR2>存储器系统章节. 位带简介 位操作就是可以单独的对一个比特 ...

  2. STM32之GPIO端口位带操作

    #ifndef __SYS_H #define __SYS_H #include "stm32f10x.h" //位带操作 //把“位带地址+位序号”转换别名地址宏 #define ...

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

    正在准备做毕业设计,配置LED_Config()的时候,又看到了位带操作的宏定义,我又嘀咕了,什么是位带操作,一年前在使用位带操作的时候,就查阅过好多资料,Core-M3也看过,但是对于博主这种“低能 ...

  4. 第13章 GPIO—位带操作

    第13章     GPIO—位带操作 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fire ...

  5. 玩转X-CTR100 l STM32F4 l 基础例程printf、LED、蜂鸣器、拨码开关、位带操作

    我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      本文介绍X-CTR100控制器基础板载资源 ...

  6. 玩转X-CTR100 | STM32F4 l GPIO位带操作

    更多塔克创新资讯欢迎登陆[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]       STM32F4位带概念,及位带的GPIO操作实践应用. 原理介 ...

  7. 关于STM32位带操作随笔

    以前在学习STM32时候关注过STM32的位带操作,那时候只是知道位带是啥,用来干嘛用,说句心里话,并没有深入去学习,知其然而不知其所以然.但一直在心中存在疑惑,故今日便仔细看了一下,写下心得供日后参 ...

  8. GPIO—位带操作

    GPIO—位带操作本章参考资料:< STM32F4xx 中文参考手册>存储器和总线构架章节. GPIO 章节,< Cortex®-M4 内核编程手册> 2.2.5 Bit-ba ...

  9. STM32位带操作

    STM32的位带操作是基于cortex内核自带的,而不是st公司独创.基本的思路就是用一个32位的地址空间访问一个bit,因为stm32只支持32位数据的读取,不像51单片机一样,是可以单独对一位操作 ...

随机推荐

  1. Hibernate update 和 merge 、saveOrUpdate的区别

    this.getSession().update(obj); this.getSession().merge(obj); this.getSession().saveOrUpdate(obj); 1. ...

  2. DotNetCore跨平台~EFCore连接Mysql的方式

    回到目录 在.net frameworks的ef里连接mysql我们已经测试通过了,而在dotnet core里的efCore上去连接mysql我们需要测试一下,并且在测试过程中出现了一些问题,当然最 ...

  3. bootstrap栅栏系统css中的col-xs-*、col-sm-*、col-md-* 的意义以及 bootstrap一个标签中,同时有 col-xs , col-sm , col-md , col-lg的理解

    摘要: bootstrap栅栏系统css中的col-xs-*.col-sm-*.col-md-* 的意义: .col-xs- 超小屏幕 手机 (<768px) .col-sm- 小屏幕 平板 ( ...

  4. 1,入门-Hello Soring Boot

    什么是SpringBoot Spring Boot是Spring社区发布的一个开源项目,旨在帮助开发者快速并且更简单的构建项目.大多数SpringBoot项目只需要很少的配置文件. SpringBoo ...

  5. 基于Windows服务的聊天程序(一)

    本文将演示怎么通过C#开发部署一个Windows服务,该服务提供各客户端的信息通讯,适用于局域网.采用TCP协议,单一服务器连接模式为一对多:多台服务器的情况下,当客户端连接数超过预设值时可自动进行负 ...

  6. Linux基础(八)

    一.shell shell一般代表两个层面的意思,一个是命令解释器,比如BASH,另外一个就是shell脚本.Python也是一种解释语言. 1.   Linux中命令是按照下面的优先级执行的 ==& ...

  7. Windows查看端口使用状况

    使用端口是我们在进行远程或者打印机等都会遇到的,但是有很多用户会遇到端口被占用的情况,遇到这样的问题首先就要找出电脑中的所以端口然后进行查看,还是有很多人不知道该如何查看电脑端口. 1 查看windo ...

  8. 【linux相识相知】磁盘分区及文件系统管理详解

    磁盘,提供持久的数据存储,它不像我们的内存,如果突然断电了,在内存中的数据一般都会被丢掉了,内存中的数据在保存的时候,会被写到硬盘里面,磁盘也是一种I/O设备. 我们都知道磁盘分区完成之后,还要进行格 ...

  9. 抓取60000+QQ空间说说做一次数据分析

    对于QQ空间的数据一直来是垂涎不已,老早就想偷过来研究研究,这几天闲下来便开始动手... 整个程序的流程为:登录-->获取cookie-->获取所有的好友qq_number-->根据 ...

  10. mysql 5.7 root密码重置(centos 7)

    mysql5.7版本之后,与mariadb不同,在安装之后,在启动之时,会进行自动随机密码的设定,所以在systemctl start mysqld之后,会出现mysql -uroot -p无法登陆的 ...