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

发布者:Jinyu521最新更新时间:2020-02-28 来源: eefocus关键字:STM32  嵌入式学习  C语言  基础复习 手机看文章 扫描二维码
随时随地手机看文章

摘要


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


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


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


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


一、位操作:

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


& 按位与 ~ 取反

| 按位或 << 左移

^ 按位异或 >> 右移

 

掌握了这六种操作否的用法,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必须了解的五大嵌入式操作系统
下一篇:嵌入式系统学习——STM32之外部中断

推荐阅读最新更新时间:2024-11-01 18:30

单片机C语言精确延时值的计算
关于单片机C语言的精确延时,网上很多都是大约给出延时值没有准确那值是多少,也就没有达到精确高的要求,而51hei给出的本函数克服了以上缺点,能够精确计数出要延时值且精确达到1us,本举例所用CPU为STC12C5412系列12倍速的单片机,只要修改一下参数值其它系例单片机也通用,适用范围宽。 共有三条延时函数说明如下: 函数调用分两级:一级是小于10US的延时,二级是大于10US的延时 //====================小于10US的【用1US级延时】==================== //----------微秒级延时--------- for(i=X;i X;i--) 延时时间=(3+5*X)/12 提示(
[单片机]
意法的90纳米STM32微控制器内置独特闪存加速器
    意法半导体宣布取得两项重大技术进展,促使市场成功的STM32系列微控制器的性能和功耗获得进一步提升,这两项进展分别是:内嵌90纳米制程闪存的微控制器问世;推出业内首款针对工业标准的ARM Cortex-M3内核优化的自适应实时(ART)存储器加速器。     意法半导体的首批采用90nm嵌入式闪存制程生产的STM32微控制器的运行速度更快,功耗更低,外设集成度更高,片上存储密度更大。90nm嵌入式闪存技术的性能已经在智能卡和汽车电子IC上得到实证。意法半导体已于2009年发布了内嵌90nm 闪存的微控制器的样片。     由于ARM Cortex-M3的性能高于闪存技术,在运行频率较高时,处理器必须等待闪存,意法半
[单片机]
STM32仿真按键控制led灯源程序
刚学习 课上做的一个小实验 保存一下 在GPIOC口,分别接有一个开关K1和两个指示灯LED1和LED2。两个灯一亮一灭,每按一下开关,两个灯的亮灭状态翻。 单片机源程序如下: #include stm32f10x.h #include led.h #include key.h int main(void) { u8 key; LED_Init(); KEY_Init(); while(1){ key = KEY_Scan(); switch(key){ case WK_UP: LED1=!LED1, LED0=!LED0; break; }
[单片机]
<font color='red'>STM32</font>仿真按键控制led灯源程序
STM32实战 2.矩阵键盘通过串口3输出
#include KEY44.h #include sys.h #include delay.h #include usart.h u8 key_num = 0; int main(void) { uart_init(115200); delay_init(); KEY44_Init(); while(1) { key_num = key44_Scan(); if(key_num != 0) { printf( KEY is %drn ,key_num); } } } #include KEY44.h #include sys.h #include delay.h vo
[单片机]
程序结构理解(1) 以stm32为例
程序中的几个段理解 有些技术文章中会直接使用RO,请注意区分RO和RO-data的区别。 Code:即代码域,它指的是编译器生成的机器指令。 RO_data:ReadOnly data,即只读数据域,它指程序中用到的只读数据,全局变量,例如C语言中const关键字定义的全局变量就是典型的RO-data。 RW_data:ReadWrite data,即可读写数据域,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。例如全局变量或者静态变量,且定义时赋予“非0值”给该变量进行初始化。 ZI_data:ZeroInitialie data
[单片机]
程序结构理解(1) 以<font color='red'>stm32</font>为例
SPI总线驱动(C语言源程序)
/*-------------------- 〖说明〗SPI总线驱动源程序 默认11.0592Mhz的晶振。 〖文件〗93CXX.C ﹫2003/5/12 ---------------------*/ /*通用93c06-93c86系列使用说明 93c06=93c4693c56=93c6693c76=93c86*/ //dipx 可以自行定义 #i nclude AT89X52.H #i nclude "intrins.h" /*----------------------------------------------------- SPI93cXX系列时序函数调用(普通封装) 调用方式:自行定义﹫2001/05/12 函数
[单片机]
STM32控制MPU6050
1.硬件连接 实验采用正点原子公司的 AN1507 ATK-MPU6050 六轴传感器模块 MPU6050 STM32 VCC --- VCC GND --- GND SDA --- PB9 SCL --- PB8 INT --- 不接 AD0 --- 不接 2. 重要寄存器 2.1 电源管理寄存器 1 DEVICE_RESET 位用来控制复位,设置为 1,复位 MPU6050,复位结束后, MPU 硬件自动清零该位 SLEEEP 位用于控制 MPU6050 的工作模式,复位后,该位为 1,即进 入了睡眠模式(低功耗),所以我们要清零该位
[单片机]
<font color='red'>STM32</font>控制MPU6050
STM32芯片+8M晶振+32.768Khz晶振的搭配选型参考方案
本文主要讲解STM32系列MCU匹配晶振的推荐选型及注意事项等。 一般来说MCU需要接入一个主频和一个时钟频率,STM32系列也不例外。 其中主频4M、8M、16M、24M、32M和48M居多,一般搭配常用日本大真空KDS、日本电波NDK 8MHZ 晶振;时钟频率即是最常见的32.768KHZ晶振。 如图所示: 一、KDS、NDK 8MHZ的主频晶振 晶振可供选择的范围很广泛,但是随着电子产品小型化越来越成为主流,电路板上提供给各位研发设计人员的空间越来越小,因此对晶振的选择也是以小体积的贴片封装为主。 本文推荐两种品牌极具性价比的8MHZ晶振,3225封装尺寸,这几年主流设计选择产品。 一种是:日本KDS大真空公司推出
[单片机]
<font color='red'>STM32</font>芯片+8M晶振+32.768Khz晶振的搭配选型参考方案
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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