嵌入式C语言基础(STM32)

这篇具有很好参考价值的文章主要介绍了嵌入式C语言基础(STM32)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:一条混迹嵌入式3年的老咸鱼,想到自己第一次接触到stm32的库函数时,c语言稀碎,痛不欲生的场景,该文章为萌新指条明路。

一、位操作

位操作在嵌入式中常用于直接对芯片的寄存器进行操作,当时作为初学者的我看着一脸懵逼,至于为什么这样修改,下面好好分析一下。

#define DHT11_IO_IN()  {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=8<<12;}
#define DHT11_IO_OUT() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=3<<12;}

 一句话有很多知识点!

1.#define 宏定义 (预处理)

#define是C语言的预处理命令,它用于宏定义,用来将一个标识符定义为一个字符串,该标识符称为宏名,被定义的字符串称为替换文本,采用宏定义的目的主要是方便程序编写。一般放在源文件的前面,称为预处理部分。这个仅仅只是程序文本进行替换,通常用于修改某个数据较为麻烦,使用#define。如果要重命名一个变量,正确操作应该是使用typedef。

用法:#define [别名] [字符串]

其中,字符串可以是常数、字符串和表达式等。
       例如:#define Temp 25
       该语句表示在程序中使用Temp的地方,编译时全部替换为25.
       例如:#define RCC AHBPeriph_DMA1 ((uint32_t)0x00000001)
       该语句表示使用RCC_AHBPeriph_DMA1,编译时全部替换为32位的无符号数据0x00000001.

 2.GPIOG->CRH(结构体指针)

#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)

有了前面的基础,应该可以看懂这里使用了宏定义,((GPIO_TypeDef *) GPIOG_BASE)定义了一个结构体指针,GPIOG_BASE是一个地址(实际上是GPIO_TypeDef 这个结构体的首地址),本质上是在GPIOG_BASE地址(APB2总线的基地址+0x2000),在该地址上创建了一个类型为GPIO_TypeDef的结构体指针,结构体如下:

#define     __IO    volatile                  /*!< defines 'read / write' permissions   */

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

在结构体中定义了7个32位的寄存器,且使用volatile关键词,目的是为了减少程序运行的错误。以后会讲到。结构体访问成员变量的方法是结构体->成员变量。通过结构体指针访问寄存器的方式来提高程序运行效率。

3.&=、|=、>>、<<(仔细思考)

&=0xffff0fff目的是把32位寄存器中的第17-20位置为0

|=0x3<<12目的是左移12bit,把0x3赋予17-20位

(1)把变量的某位清零

//对 bit2 清零
a  &= ~  (1<<2);

(2)把变量的某几位清零

//对bit 2、3清零

a &= ~ (3<<2*1); 

(3)对变量的某几位进行赋值

//对bit 4置1

a |= (1<<2*2);

(4)对变量的某位取反

//对bit 6取反

a ^=(1<<6);

二、Typedef的妙用(变量重命名)

typedef signed char int8_t;//为数据类型signed char起别名int8_t
typedef signed int int32_t;//为数据类型signed int起别名int32_t

typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

typedef struct
{
   uint16_t GPIO_ Pin;
   GPIOSpeed_TypeDef GPIO_Speed;
   GPIOMode TypeDef GPIO_Mode;
}GPIo_InitTypeDef;

1.typedef的基本应用

        为已知的数据类型起一个简单的别名,如上例int32_t。

2. typedef 与结构体struct结合使用
       该用法用于自定义数据类型。0如 stm32f10x_gpio.h头文件中的GPIO初始化结构体GPIO_InitTypeDef。因此GPIO_InitTypeDef成为一个结构体变量名词,可以创造新的变量来完成初始化操作。结构体访问成员变量的方式不同于结构体指针,所以GPIO_InitTypeDef其实是一个结构体

方法:结构体.成员变量

 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 /	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //¸
 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 Êä³ö¸ß

三、初始化函数分析

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

(1)形参指针

        成员变量是GPIO_TypeDef类型的指针和GPIO_InitTypeDef类型的指针,为什么这里要使用指针呢?因为如果使用GPIO_TypeDef  GPIOx效果也是一样的呀,也可以访问其程序变量。传数据拷贝的是内存单元的数据,如果数据很多的话拷贝过来都要为它们分配内存。而且传数据非常消耗效率,为形参分配内存需要时间,拷贝需要时间,最后结束了返回还是需要时间。问题是传值会让函数重新开辟一个结构体,而传指针可以不用开辟新的地址。大大减少了内存消耗,提高程序运行速率。

所以使用结构体指针作为形参,不仅可以读取结构体保存的数据,还可以提高效率,还有一个知识点。

(2)什么时候需要使用&(取址符号)

结论:如果变量是数组或者结构体,就不需要使用取址符号;若变量是一个数,就需要加取址符号,至于为什么,自己可以好好想想指针的意义。

(3)感受C语言的魅力(结构体和结构体指针)

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
    /* Check the parameters */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
}

四、static关键词

static关键字可以用来修饰变量,使用static关键字修饰的变量,称为静态变量。
      静态变量的存储方式与全局变量一样,都是静态存储方式。全局变量的作用范围是整个源程序,当一个源程序由多个源文件组成时,全局变量在各个源文件中都是有效的,即一个全局变量定义在某个源文件中,若想在另一个源文件中使用该全局变量,则只需要在该源文件中通过 extern关键字声明该全局变量就可以使用了。若在该全局变量前加上关键字static,则该全局变量被定义成一个静态全局变量,其作用范围只在定义该变量的源文件内有效,其他源文件不能引用该全局变量,这样就避免了在其他源文件中因引用相同名字的变量而引发的错误,有利于模块化程序设计。

在自己写程序时,很少会用到,了解其用法就行。如果是写linux的设备驱动,那就另当别论。

五、寄存器地址

找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x40010C0C, ODR 寄存器是 32bit,低 16bit 有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。我们通过 C 语言指针的操作方式,让 GPIOB的 16 个 IO 都输出高电平,代码如下:
 

*(unsigned int*)(0x4001 0C0C) = 0xFFFF;

注:本质上,指针存放的是一个地址。0x4001 0C0C 在我们看来是 GPIOB 端口 ODR 的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针(一个地址),即 (unsigned int *)0x4001 0C0C。再加上*后,变成了* 地址=0xffff的形式,和指针最本质的形式相似,相当于直接对0x4001 0C0C这个地址进行赋值。
 

(未完待续......)
 文章来源地址https://www.toymoban.com/news/detail-434217.html

到了这里,关于嵌入式C语言基础(STM32)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式学习stm32基础知识(期末复习)

    1. 计算机的体系架构 冯诺依曼架构 ​ 在完整的计算机系统中,包含五个部分,储存器,运算器,控制器输入设备和输出设备。 改进的冯诺依曼架构 改进型架构的各模块的高速数据交换中心利用储存器这个大容量,极大的提高了效率。 哈佛架构 ​ 哈佛结构数据空间和地址

    2024年02月07日
    浏览(71)
  • 嵌入式学习笔记——STM32硬件基础知识

    上一篇中我们重点是讲了一下怎么搭建开发环境以及怎么下载烧录的过程,这都是解决的电脑端的开发环境问题,还没有到实际的开发板上,我们的单片机是都是焊接在开发板上的,PCB上有着它所需的工作电路。并不是直接给供电电压就可以让其工作的,本文主要是简介一下

    2024年01月22日
    浏览(75)
  • 嵌入式-stm32重要基础知识(及踩过的坑)

    1、固件库: 固件库就是函数的集合,固件库函数的作用是向下负责与寄存器直接打交道, 向上提供用户函数调用的接口(API)。 2、CMSIS标准与固件库的关系 就是对固件库函数的要求。 ARM 公司为了能让不同的芯片公司生产的 Cortex-M3 芯片能在软件上基本兼容,和芯片生产商

    2024年02月04日
    浏览(60)
  • 蓝桥杯嵌入式基础模块——LCD显示器的基本使用(新板)STM32G431(HAL库开发)

            在蓝桥杯嵌入式官方给我们提供好了,LCD显示的底层源码,我们只需要,记住里面的API函数,会用这些函数就行。         在官方给的资料中找到这个文件名字DK117_G4 Data Packet-开发板驱动文件里面就是所有的底层文件有两种类型,一种是基于HAL库的一种是标准库

    2024年02月09日
    浏览(62)
  • stm32嵌入式实验考核

    STM32 实验考核题目 1. 利用 STM32 小板实现:控制外接 LED 灯每隔 3 秒钟亮暗变换,同 时在 PC 机上显示 MCU 的计时时间,MCU 的初始时间由 PC 机 方设置。 2. 利用 STM32 小板实现:利用导线外接 GPIO 口模拟 2 个按键输入, 根据输入组合的四种情况,分别控制三色灯四种流水灯效果

    2024年02月03日
    浏览(52)
  • 嵌入式 STM32 通讯协议--MODBUS

    目录 一、自定义通信协议 1、协议介绍 2、网络协议 3、自定义的通信协议  二、MODBUS通信协议 1、概述 2、MODBUS帧结构  协议描述 3、MODBUS数据模型   4、MODBUS事务处理的定义 5、MODBUS功能码  6、功能码定义   7、MODBUS数据链路层 8、MODBUS地址规则  9、MODBUS帧描述 10、MODBUS两种

    2024年02月11日
    浏览(65)
  • STM32的时钟系统(嵌入式学习)

    时钟是指用于计量和同步时间的装置或系统。时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,例如:串口数据的发送、AD转换、定时器计数等。因此时钟对于计算机系统是至关重要的,通常时钟系

    2024年02月16日
    浏览(50)
  • 嵌入式——新建STM32工程(标准库)

    目录 一、初识标准库 1.CMSIS标准及库层级关系 2.库文件介绍 (1)Libraries文件夹 ①CMSIS文件夹 ②STM32F10x_Std_Periph_Driver文件夹 ③ 在用库建立一个完整的工程时,还需要添加stm32f10x_it.c、 stm32f10x_conf.h 和 system_stm32f10x.c文件 (2)Project文件夹 (3)Utilities文件夹 3.库各文件之间的关

    2024年01月23日
    浏览(59)
  • STM32串口通信详解(嵌入式学习)

    时钟信号在电子领域中是指用于同步和定时电路操作的周期性信号。它在数字系统和通信系统中起着至关重要的作用,用于协调各个组件之间的数据传输和操作。 时钟信号有以下几个重要的方面: 频率:时钟信号的频率是指单位时间内信号周期的数量。它通常以赫兹(Hz)为

    2024年02月09日
    浏览(69)
  • 嵌入式项目分享 stm32智能运动计步系统 - 物联网 嵌入式 单片机

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月20日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包