写在前面的话
嵌入式开发涉及的层面很广,它既有底层硬件的开发,又涉及上层应用的开发,也就是所谓的系统集成的硬件和软件。而C语言不仅具有汇编语言操作底层的优势,又具有高级开发语言的功能性强的特点,当之无愧地成为了嵌入式开发中的主流语言。
所以,”在STM32开发过程中,不论是基于寄存器开发还是基于库开发,深入理解和掌握嵌入式C语言的函数、指针、结构体,这是学习STM32的关键“
嵌入式为何物?
网上一搜,教材一翻,关于嵌入式的简介满天飞,分析的很专业,解释的很透彻,但是…看不懂
伟大的现代哲学家吕小布说过,所谓教材就是把一堆简单易懂的名词扩写成一堆谁也看不懂的句子。
“沿波讨源,虽幽必显”,嵌入式其实就在我们身边,一些小家电,电饭煲、热水壶、冰箱、空调、洗衣机等,在这些琳琅满目的家电里面有那么一块电路板,上面焊满了各式各样的电子元器件和芯片。如果这些家电需要带上一些**“智能化”**功能如按键、显示屏、定时、感应等功能,则需要加一个可编程芯片,这个芯片就是单片机,也是微控制器,是这个电路板的大脑,单片机编程,从某种意义来说就是嵌入式开发
STM32的数据类型
- 数据是嵌入式中C语言的基本操作对象,数据类型指的是数据在计算机内的存储方式
一些基本类型如下图:
- 由于不同CPU定义的数据类型的长度不同,因此ARM公司联合其他半导体厂商制定了统一的CMSIS 软件标准,这个标准中预先定义了相关的数据类型,ST公司也为开发人员提供了基于C语言的标准外设库 :
表1 STM32定义的数据类型
C | STM | 功能 |
---|---|---|
unsigned char | uint8_t | 0~255 |
unsigned short int | uint16_t | 0~65535 |
unsigned int | uint32_t | 0 ~ (2^32) - 1 |
unsigned long long | uint64_t | 0 ~ (2^64) - 1 |
signed char | int8_t | (-128) ~ (+127) |
signed short int | int16_t | (-32768) ~ (+32767) |
signed int | int32_t | (-2^31) ~ (+2^31) - 1 |
signed long long | int64_t | (-2^64) ~ (+2^64) - 1 |
表2 STM32的IO类型限定词
IO类型限定词 | 类型 | 说明 |
---|---|---|
_I | Volatile const | 只读 |
_O | volatile | 只写 |
_IO | volatile | 读写 |
C语言中常用的关键字
1. const
- const关键字用于定义只读的变量,其值在编译时不能被改变,注意,const关键字定义的是变量而不是
- 使用 const关键字是为了在编译时防止变量的值被误修改,同时提高程序的安全性和可靠性,一般放在头文件中或者文件的开始部分
- const关键字定义的变量是全局变量
- const 关键字与#definc关键字存在区别,#define关键字只是简单的文本替换,而const关键字定义的变量是存储在静态存储器中的 。
- 使用**#define**关键字定义常量的形式为
#define PI 3.14159
/*使用该方式定义后,无论在何处使用PI,都会被预处理器以3.14159替代,
编译器不对PI进行类型检查,若使用不慎,则很可能由预处理引入错误,
且这类错误很难发现。*/
用const声明变量的方式虽然增加了分配空间,但可以很好地消除预处理引入的错误,并提供了良好的类型检查形式,保证安全性。
-
利用 const关键字进行编程时需要注意以下三点 :
- 使用const关键字声明的变量,只能读取,不能被赋值。 如:
const uint8t sum = 3.14; uint8_t abs=0; ... sum= abs; //非法,将导致编译错误,因为sum 只能被读取,不能赋值 abs- sum: //合法
- const关键词修饰的变量在声明时必须初始化,上述语句表示 sum值是3.14,且sum值在编译时不能修改,若在编译过程中直接修改sum值,则编译器会提示出错。
- 函数的形参声明为const,则意味着所传递的指针指向的内容只能读,不能被修改。如C语言的标准函数库中用于统计字符串长度的函数 int strlen(const char*str)。
2.static关键字
static关键字可以用来修饰变量,使用static关键字修饰的变量,称为静态变量。
-
静态变量的存储方式与全局变量一样,都是静态存储方式
-
全局变量的作用范围是整个源程序,当一个源程序由多个源文件组成时,全局变量在各个源文件中都是有效的
-
即一个全局变量定义在某个源文件中,若想在另一个源文件中使用该全局变量,则只需要在该源文件中通过 extern关键字声明该全局变量就可以使用了
-
若在该全局变量前加上关键字static,则该全局变量被定义成一个静态全局变量,其作用范围只在定义该变量的源文件内有效,其他源文件不能引用该全局变量,这样就避免了在其他源文件中因引用相同名字的变量而引发的错误,有利于模块化程序设计。
- static关键字不仅可以用来修饰变量,而且可以用来修饰函数。模块化程序设计中,若用static声明一个函数,则该函数只能被该模块内的其他函数调用
#include "stm32f1xx_hal .h”
static void DMA_SetConfig (DMA_HandleTypeDef *hdma,uint32_t SrcAddress,uint32_t DstAddress, uint32_t DataLength);
...
HAL_statusTypeDef HAL_DMA_start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
HAL_StatusTypeDef status- HAL_OK;”
.... ...
if(HAL_DMA_STATE_REA.DY m- hdma->state)
{
DMA_Setconfig(hdma, SrcAddress, DstAddress, DataLength);
... ...
}
... ...
}
/*上述代码为DMA模块的源文件stm32f1xx_hal_dma.c,
若利用static将DMA_SetConfig()函数声明为一个静态函数,
则 DMA_SetConfig)函数只能被stm32flxx_hal_dma.c中的其他函数调用,
而不能被其他模块的文件使用,即定义了一个本地函数,
有效避免了因其他模块的文件定义了同名函数而引发的错误,
充分体现了程序的模块化设计思想*/
-
- static除了用于定义静态全局变量,还用于定义静态局部变量,保证静态局部变量在调用过程中不被重新初始化。典型应用案例有实现计数统计功能。
void fun_count()
{
static count_num=0;
//声明一个静态局部变量,count_num用作计数器,初值为0
count_num++;
printf("%d\n",count_num) :
}
int main(void)
(
int i=0;
for( i=0;i<=5;i++)
{
fun_count();
}
return 0;
}
/*在main函数中每调用一次 fun_count()函数,静态局部变量count_num加1,
而不是每次都被初始化为初值0。*/
3.volatile关键字
volatile关键字,它是一个类型修饰符,含义为“易变的”
volatile char i;
/*使用volatile关键字定义了一个字符型的变量i,指出i是随时可能发生变化的,
每次使用该变量时都必须从i的地址中读取*/
- 由于内存的读/写速度远不及CPU中寄存器的读/写速度
- 为了提高数据信息的存取速度
- 一方面在硬件上引入高速缓存Cache
- 另一方面在软件上使用编译器对程序进行优化 – 将变量的值提前从内存读取到CPU的寄存器中,以后用到该变量时,直接从速度较快的寄存器中读取
- 这样有利于提高运算速度,但同时也可能存在风险:
- 如该变量在内存中的值有可能被程序的其他部分(如其他线程)修改或覆盖
- 而寄存器中存放的仍是之前的值,这就导致应用程序读取的值和实际变量值不一致
- 也有可能是寄存器中的值发生了改变,而内存中该变量的值没有被修改,同样也会导致不一致的情况发生
- 为防止由于编译器对程序进行优化导致读取错误数据,使用 volatile关键词进行定义
- 使用volatile关键字就是不让编译器进行优化,即每次读取或者修改值时,都必须重新从内存中读取或者修改,而不是使用保存在寄存器的备份。
- 使用 volatile关键字定义变量后,该变量就不会因外因而发生变化了 :
- 中断服务程序中修改的、供其他程序检测的变量需要使用volatile关键字
- 多任务环境下各任务间共享的标志应添加 volatile关键字
- 外设寄存器地址映射的硬件寄存器通常要用volatile关键字进行声明
4.extern关键字
extern关键字用于指明此函数或变量定义在其他文件中,提示编译器遇到此函数或变量时到其他模块中寻找其定义
extern关键字声明的函数或变量就可以在本模块或其他模块中使用,因此,使用extern关键字是一个声明而不是重新定义
extern int a;
/*第一条语句仅仅是变量a的声明,而不是定义变量a,并未为a分配内存空间,
变量a作为全局变量只能被定义一次*/
extern int funA( ):
/*第二条语句声明函数funA(),此函数已在其他文件中定义*/
下期预告
今天简单介绍了嵌入式开发的几个方向和几个常见的关键字介绍,其实很多知识都是我从别的博主中的博客中看到的,然后进行一个搬运总结,下期继续回顾结构体等C语言常用类型
万事开头难,所有的领域在学习的初期都是一个从无到有的过程,独自探索这个过程无疑是非常枯燥且痛苦的,没有结果产出的喜悦,也没有醍醐灌顶的振奋。
慢慢来吧,先搞起来再说,希望在这个领域,能遇到许多不以山海为远的相知者,一起交流