STM32 嵌入式学习入门(0)——C语言基础复习

发布者:和谐共融最新更新时间:2019-09-11 来源: eefocus关键字:STM32  学习入门  C语言  基础复习 手机看文章 扫描二维码
随时随地手机看文章

摘要


主要介绍了嵌入式编程中几个常用,但软件编程中用得不是很多的C语言知识。包括位操作、条件编译、结构体和结构体指针、typedef声明类型、以及extern变量声明、static关键字等内容。


本文并没有将相关C语言知识点介绍地很详细,毕竟这么多知识点要想掌握绝对不是看几篇文档就能掌握的。因此博主建议,如果上述的C语言知识掌握得还不是很好的话,找一本C语言的书好好研究研究。尤其是结构体和结构体指针、还有函数的知识(本文没提到),一定要很熟练。


本文除了简要介绍C语言知识,也结合博主自己的感受简单谈了各个知识点用在了嵌入式编程的什么地方,有不详细和描述不准确的地方欢迎大家留言讨论。


要想学习STM32,C语言的基础是必须的。除了最基本的C语言的语法,如循环、判断、数组、结构体、函数、指针这些软件编程常用的知识外,还包括位操作、条件编译、结构体指针、typedef声明类型、以及extern变量声明、static关键字等常用内容。这里结合实际代码分析一下这些知识点,如果想完整系统地了解这些C语言知识,大家可以翻翻C语言教材,比如《C Primer Plus》(第六版)这本书,尤其对于位操作的知识讲得很详细。


一、位操作:

位操作简单说就是指对基本类型变量可以在位级别进行操作。下面先看几种位操作符:


image.png

掌握了这六种操作否的用法,C语言的位操作就差不多了。这六种操作符的解释如下:


1. & 按位与: 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0。//   1&1 = 1   1&0 = 0   0&1 = 0   0&0 = 0


2.|按位或:两个相应的二进制位中只要有一个为1,该位的结果值为1。//   1|1 = 1   0|1 = 1   1|0 = 1   0|0 = 0 


3.^按位异或: 若参加运算的两个二进制位值相同则为0,否则为1。//   1^1 = 0   0^1 = 1   1^0 = 1   0^0 = 0


4.!取反:  对一个二进制数按位取反,即将0变1,将1变0。//    1! = 0    0! = 1


5.<<左移:用来将一个数的各二进制位全部左移N位,右补0。//   00001100 << 2 = 00110000


6.>>右移:将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0。//   00001100 >> 2 = 00000011


 


下面介绍一些用寄存器开发STM32时候实用的位操作技巧:


1)不改变其他位的值的状况下,对某几个位进行设值。

这个场景单片机开发中经常使用,方法就是先对需要设置的位用 & 操作符进行清零操作,然后用 | 操作符设值。比如我要改变GPIOA的状态,可以先对寄存器的值进行 & 清零操作

然后再与需要设置的值进行 | (或运算)。


GPIOA->CRL&=0XFFFFFF0F; //将第 4-7 位清 0

GPIOA->CRL|=0X00000040; //设置相应位的值,不改变其他位的值 (将CRL寄存器第7位设置为1)


2)取反操作使用技巧

SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为 0,同时其他位都保留为 1,简单的作法是直接给寄存器设置一个值:

TIMx->SR=0xFFF7;

这样的做法设置第3位为0,但是这样的作法同样不好看,并且可读性很差。看看库函数代码中怎样使用的:

TIMx->SR = (uint16_t)~TIM_FLAG;



而 TIM_FLAG 是通过宏定义定义的值:

#define TIM_FLAG_Update ((uint16_t)0x0001)

#define TIM_FLAG_CC1 ((uint16_t)0x0002)

看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_Update 就是设置的第 0 位了,可读性非常强。


注:在STM32的开发中,更多的时间可能会直接使用官方的库函数,库函数实际上是将复杂的寄存器封装了一下。使用库函数可以避免复杂的位操作,使代码更具有可读性,但同样的项目,使用库函数其代码量可能会比直接通过操作寄存器写出来的工程的代码量稍微多一点,执行效率可能会稍微低一点,当然这只是一点点…………


学习STM32的时候要从寄存器上去理解原理,理解实现过程,但是如果真的需要做一个嵌入式项目,可能用库函数去开发比较方便,效率更好一点,这是博主自己的感受和观点。


 


二、条件编译:

单片机程序开发过程中,经常会遇到一种情况, 当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。 条件编译命令最常见的形式为:


#ifdef 标识符

程序段 1

#else

程序段 2

#endif

它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译,否则编译程序段 2。 其中#else 部分也可以没有,即:


#ifdef

程序段 1

#endif

这个条件编译在 MDK 里面是用得很多的,在 stm32f10x.h 这个头文件中经常会看到这样的语句:


#ifdef STM32F10X_HD

大容量芯片需要的一些变量定义

#end

而 STM32F10X_HD 则是我们通过#define 来定义的。


条件编译理解起来也不是很困难,可以类比于C语言中的 if-else 语句去理解。条件编译在STM32的开发中还是比较常用的。自己写代码写 .h 文件的时候开头会用到。此外就是要能看懂库函数里面的条件编译了。


 


三、结构体和结构体指针:

结构体是C语言中的基础知识,同时结构体和结构指针也是STM32开发中非常重要的东西,尤其在使用库函数的时候,库函数中很多函数的入口参数中都有结构体指针,所以如果我们要调用这种函数,就先在主调函数中声明一个结构体变量,然后对这个结构体变量的各个成员赋值,最后再调用相关函数,调用的时候看清楚函数原型,入口参数是结构体类型还是结构体指针,不要搞错了。这里再多说两句,这里的结构体每个成员可以赋的值往往都是通过枚举或者宏定义确定好的,不能自己乱写,而应该去查找宏定义部分的代码,选定需要的那个枚举字面值作为结构体相关成员的值。


关于结构体和结构体指针的例子可以看GPIO的初始化,这里就不再多说了:STM32 GPIO的介绍


 


四、typedef声明类型:

如果学过数据结构,相信对typedef也不陌生。用typedef的一个好处就是使代码的可读性更高,写代码也更方便。typedef 在代码中用得最多的就是定义结构体的类型别名和枚举类型了。


 


struct _GPIO

{

__IO uint32_t CRL;

__IO uint32_t CRH;

};

定义了一个结构体 GPIO,这样我们定义变量的方式为:


struct _GPIO GPIOA;//定义结构体变量 GPIOA

但是这样很繁琐, MDK 中有很多这样的结构体变量需要定义。这里我们可以为结体定义一个别名 GPIO_TypeDef,这样我们就可以在其他地方通过别名 GPIO_TypeDef 来定义结构体变量了。方法如下:


typedef struct

{

__IO uint32_t CRL;

__IO uint32_t CRH;

} GPIO_TypeDef;

Typedef 为结构体定义一个别名 GPIO_TypeDef,这样我们可以通过 GPIO_TypeDef 来定义结构体变量:


GPIO_TypeDef GPIOA,GPIOB;

 


这里的 GPIO_TypeDef 就跟 struct _GPIO 是等同的作用了。 这样是不是方便很多? 


除了用在结构体上,typedef类型别名也大量用在int、short等这种变量上, 所以写STM32代码的时候几乎就不会出现类似于定义int型变量这样的语句,全部用 u8、u16这样的量代替了,比如u16代表的就是一个无符号的16位整型数据(这一个描述可能有一点偏差)。


 


五、extern关键字:

C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于 extern 申明变量可以多次,但定义只有一次。在我们的代码中你会看到看到这样的语句:


extern u16 USART_RX_STA;

这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,在这里要使用到。所以,你肯定可以找到在某个地方有变量定义的语句:


u16 USART_RX_STA;


嗯,extern关键字,说实话,博主自己写代码确实没用过。So……


关键字:STM32  学习入门  C语言  基础复习 引用地址:STM32 嵌入式学习入门(0)——C语言基础复习

上一篇:STM32 嵌入式学习入门(2)——STM32的GPIO介绍
下一篇:STM32 嵌入式学习入门(1)——STM32简介及STM32学习方法简介

推荐阅读最新更新时间:2024-11-18 02:31

基于STM32从零写操作系统系列---点亮LED灯
目的: 用汇编语言编写一个点亮LED的程序,用于验证前面章节中,下载.bin文件到芯片后,程序能否运行。 要求: 1.芯片手册 2.开发板上有LED模块 实验步骤: 1.新建文件夹led_proj,复制bootloader_proj(参照本系列第3篇文章)文件夹中的所有文件到led_proj文件夹。 2.新建文件led.s,编写代码。代码内容主要是使能GPIO口的时钟,配置GPIO口,向GPIO口的输出数据寄存器写数据。 3.修改main.s文件,添加一条跳转指令,修改死循环代码。 4.修改makefile文件,在OBJ变量中添加led.o,如图: 5..保存所有修改,在命令行中,进入led_proj
[单片机]
基于<font color='red'>STM32</font>从零写操作系统系列---点亮LED灯
STM32 高级定时器 4种触发输入模式
IC1是输入捕获通道1. TI1,是定时器输入通道1. 他们可以有个交错的关系,比如IC1,可以输入到TI2,也可以输入到TI1,所以得设置一下.
[单片机]
<font color='red'>STM32</font> 高级定时器 4种触发输入模式
STM32学习笔记1——建立一个工程
这个寒假有幸在学长的帮助下接触到arm,作为单片机还没学明白的我,51基础也就是几节郭天祥老师的视频,现在学习arm也是一头雾水。不过也学了这么多天了,需要整理下思路来看看自己学了什么了。好了,废话不多说,开始我的arm的学习之路。 准备条件 1.一块开发板。(笔者认为这是非常重要的,方便你能随时对你的代码进行下载调试。笔者手上的是一块最新的ARM CORTEX M0内核的STM32F091RC开发板)。 2.一个顺手的IDE。(对于软件,我的观点一向是最新的就是最好用的,所以我用的是KEIL的5.13版本,在此附上 ](%28https://www.keil.com/demo/eval/arm.htm#DOWNLOAD%29
[单片机]
<font color='red'>STM32</font><font color='red'>学习</font>笔记1——建立一个工程
STM32下载出现no target connect
今天焊好了一个STM32F107VCT6的板子,一开始芯片可以下载进去程序。 然后过了5分钟,程序突然下载不进去了。然后就是一顿分析。当然分析的过程很复杂。 前前后后焊了3个芯片。STlink和Jlink都试过还是不能下载程序。 分析了一下,是芯片写保护了。 至于为什么写保护,是STlink供电不稳定, 当时引脚没有焊上去。供电断断续续的,芯片就被写保护了。解决办法在下面。 下两个图是我用STlink做的。 然后查资料将复位引脚拉低以后,然后在设备里面出现了 但是现在是下载不进去程序的。 然后继续看资料,将BOOT0引脚拉高使用ISP下载模式(就是直接下载就行了), 如果出现
[单片机]
<font color='red'>STM32</font>下载出现no target connect
STM32之电量采集
一、简介 本文介绍如何在STM32上使用ADC1的第9通道,对电池电量进行采集。 二、实验平台 库版本:STM32F10x_StdPeriph_Lib_V3.5.0 编译软件:MDK4.53 硬件平台:STM32开发板(主芯片stm32f103c8t6) 仿真器:JLINK 三、版权声明 四、硬件原理 1)硬件原理图 由上图可知通过PB1口采集电压,其中采集的电压为锂电池经过分压后的电压。 锂电池电压范围为3V~4.2V。 2)采集电压引脚的通道 其中ADC12_IN9,是指可以是ADC1的第9通道,也可以是ADC2的第9通道。 五、基础知识 1、stm32f103c
[单片机]
<font color='red'>STM32</font>之电量采集
STM32单片机设置PWM输出完整教程
STM32单片机 PWM 环境: 主机:XP 开发环境:MDK4.23 MCU:STM32F103CBT6 说明: 使用内部8M晶振,倍频到64M供给TIM3定时器,PA6(通道1)上产生640K,50%方波 源代码: 初始化时钟: //初始化RCC时钟 voidinit_rcc(void) { //将外设RCC寄存器重设为缺省值 RCC_DeInit(); //内部晶振使能 RCC_HSICmd(ENABLE); //等待工作稳定 while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY)==RESET); //LSI的启动,提供给看门狗时钟 RCC_LSICmd(ENABLE);//打开LSI w
[单片机]
stm32 spi从模式配置解答
目标:stm32(战舰) 和 stm32(迷你)的spi对传(战舰做从,迷你做主) 结果:对传成功 代码不写,你网上搜索到的可以用; 但提示:我用的是(主从片选都是软件配置); 关键点介绍:重要的是极性和相位的配置(stm32的spi对传有问题); 我stm32迷你用的极性和相位选的都为0(即Low和边沿1触发);stm32战舰极性和相位选的为1(即High和边沿2触发) 过程介绍:开始我配置着相同的极性和相位,发现Master发送4bytes数据,A1,A2,A3,A4,slave接收到的数据是D0,D1,51,D2; 经如下计算: A1 A2 A3 A4 1010 0001 1010 0010
[单片机]
STM32库函数之断言
我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用。下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以然。 4 断言机制函数assert_param 我们在分析库函数的时候,几乎每一个函数的原型有这个函数assert_param();下面以assert_param(IS_GPIO_ALL_PERIPH(GPIOx));为例说一下我的理解,函数的参数IS_GPIO_ALL_PERIPH(GPIOx),我们可以寻找到原型 #define IS_GPIO_ALL_PERIPH(PERIPH) (((*(uint32
[单片机]
<font color='red'>STM32</font>库函数之断言
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved