介绍一下单片机开发的C语言使用,C语言知识博大精深,基础还是需要系统学一下的,这里由于篇幅有限

1.1.1 基本数据类型
数据类型就是一个数据的存储类型,体现为占据空间的大小。单片机的 C 语言中常用的基本数据类型如下:

注意:当数据类型在运算时不同会进行类型转换,优先级如下:
bit→char→int→long→float→signed→unsigned
也就是说,当 char 型与 int 型进行运算时,先自动对 char 型扩展为 int 型,然后与 int 型进行运算,运算结果为 int 型。
补充:C51扩充数据类型主要如下(在文件“reg51.h”中):

1.1.2 常量与变量
C语言运算量分为常量和变量,常量其实和变量没有多大区别,有名字,占据存储空间,可以是任何的基本类型,但只有一点不同,常量的值不允许变更,通常用const关键字定义常量,如下所示:
补充:
- 布尔型变量
_Bool类型长度为1,只能取值范围为0或1。将任意非零值赋值给_Bool类型,都会先转换为1,表示真。将零值赋值给_Bool类型,结果为0,表示假。如下所示变量MCU_IO1只能取0或者1:
- 位变量
在 C51 中,允许用户通过位类型符定义位变量。位类型符有两个:bit 和 sbit。可以定义两种位变量。bit 位类型符用于定义一般的可位处理位变量。sbit 位类型符用于定义在可位寻址字节或特殊功能寄存器中的位,定义时须指明其位地址,可以是位直接地址,可以是可位寻址变量带位号,也可以是特殊功能寄存器名带位号。格式如下:
- 特殊功能寄存器变量
在 C51 中,允许用户对特殊功能寄存器进行访问,访问时须通过 sfr 或sfr16 类型说明符进行定义,定义时须指明它们所对应的片内 RAM 单元的地址。sfr 用于对 51 单片机中单字节的特殊功能寄存器进行定义,sfr16 用于对双字节特殊功能寄存器进行定义。特殊功能寄存器名一般用大写字母表示。地址一般用直接地址形式。格式如下:
1.1.3 存储属性
- register
使用 register 定义的变量称为寄存器变量。它定义的变量存放在 CPU 内部的寄存器中,处理速度快,但数目少。C51 编译器编译时能自动识别程序中使用频率最高的变量,并自动将其作为寄存器变量,用户可以无需专门声明。 - auto
使用 auto 定义的变量称为自动变量,其作用范围在定义它的函数体或复合语句内部,当定义它的函数体或复合语句执行时,C51 才为该变量分配内存空间,结束时占用的内存空间释放。自动变量一般分配在内存的堆栈空间中。定义变量时,如果省略存储种类,则该变量默认为自动(auto)变量。 - static
使用 static 定义的变量称为静态变量。它又分为内部静态变量和外部静态变量。在函数体内部定义的静态变量为内部静态变量,它在对应的函数体内有效,一直存在,但在函数体外不可见,这样不仅使变量在定义它的函数体外被保护,还可以实现当离开函数时值不被改变。外部静态变量上在函数外部定义的静态变量,它在程序中一直存在,但在定义的范围之外是不可见的。如在多文件或多模块处理中,外部静态变量只在文件内部或模块内部有效,内部静态变量是全局变量 - extern
使用 extern 定义的变量称为外部变量。C语言中extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于 extern 申明变量可以多次,但定义只有一次。 - typedef针对标识符
typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef 在 MDK 用得最多的就是定义结构体的类型别名和枚举类型了。
补充:存储器类型
存储器类型是用于指明变量所处的单片机的存储器区域情况。存储器类型与存储种类完全不同。C51编译器能识别的存储器类型有以下几种,见表所示:
- 赋值运算符
变量=表达式;
- 算术运算符
- 关系运算符
- 逻辑运算符
注意:&&左右都得成立为真;||左右有一个成立为真
- 位运算符
注意:~是按位取反;!是逻辑取反。
- 其他运算符
1)复合赋值运算符
C语言中支持在赋值运算符“=”的前面加上其它运算符,组成复合赋值运算符。如“+=”,a+=b等价于a=a+b
2)逗号运算符
在C语言中,逗号“,”是一个特殊的运算符,可以用它将两个或两个以上的表达式连接起来,称为逗号表达式。
3)条件运算符
条件运算符“?:”是C语言中唯一的一个三目运算符,它要求有三个运算对象,用它可以将三个表达式连接在一起构成一个条件表达式。其功能是先计算逻辑表达式的值,当逻辑表达式的值为真(非 0 值)时,将计算的表达式 1 的值作为整个条件表达式的值;当逻辑表达式的值为假(0 值)时,将计算的表达式 2 的值作为整个条件表达式的值。条件表达式的一般格式为:
逻辑表达式?表达式 1:表达式 2
顺序结构是最基本、最简单的结构,在这种结构中,程序由低地址到高地址依次执行,即程序是从上执行到下。除了顺序结构,还有选择结构和循环结构
1.3.1 选择结构
选择结构可使程序根据不同的情况,选择执行不同的分支,在选择结构中,程序先都对一个条件进行判断。当条件成立,即条件语句为“真”时,执行一个分支,当条件不成立时,即条件语句为“假”时,执行另一个分支。如图:

在C中,实现选择结构的语句为 if/else,if/else if 语句。另外还支持多分支结构,多分支结构既可以通过 if 和 else if 语句嵌套实现,可用 swith/case 语句实现。
- if-else语句
- switch语句
使用break关键字跳出switch语句,若没有,则会顺次执行后面的语句,直到遇到 break 或结束。
1.3.2 循环结构
在程序处理过程中,有时需要某一段程序重复执行多次,这时就需要循环结构来实现,循环结构就是能够使程序段重复执行的结构。循环结构可以用while和for语句,如下:

- while
while语句特点是先判断条件,后执行循环体。循环语句可以使用continue结束本次循环进入下一次循环。也可以使用break直接跳出循环体,执行循环体后面的语句。如:
- do-while
do-while语句特点是先执行循环体,后判断条件,如
- for循环
for循环执行流程:
①表达式1->表达式2->语句->表达式3
②表达式2->语句->表达式3
③一直循环到表达式2不成立停止循环
C语言函数可以将一个常用的功能封装成API,给以后需要时来调用,函数定义如下:
函数类型是函数返回类型,要配合return,如:
如果函数类型是void即空类型,可以不用加return返回。修饰符在C51中常用的就是interrupt m 修饰符,m 的取值为 0~31代表中断向量表,如
注意:当在一个C文件调用另一个C文件定义的函数时,除了要加头文件,还需要用extern来申明一下函数,函数原型一般形式如下:
数组在内存里是以堆栈形式存在的,它可以截取一段空间来存取固定数据类型的数据。一维数组定义形式如下:
数组的数据类型可以是数值也可以是字符,如下:
注意:除了一维数组还有高维数组,比如二维数组一般用来进行矩阵运算,这里就不展开了
1.6.1 指针的定义
指针是C语言中的一个十分重要的概念,在C中的数据类型中专门有一种指针类型。指针为变量的访问提供了另一种方式,变量的指针就是该变量的地址,还可以定义一个专门指向某个变量的地址的指针变量(指针即地址)。C中提供了两个专门的指针运算符:
指针运算符“*”放在指针变量前面,通过它实现访问以指针变量的内容为地址所指向的存储单元。取地址运算符“&”放在变量的前面,通过它取得变量的地址,变量的地址通常送给指针变量。例如:
1.6.2 指针数组与数组指针
- 指针数组
指针数组本质上还是一个数组,只是数组的元素是指针形式,如下:
- 数组指针
数组指针首先是一个指针,只不过这个指针是一个数组,可以理解为指向数组的指针
1.6.3 函数指针与指针函数
- 函数指针
函数指针是指向函数的指针变量,即本质是一个指针变量。它允许通过变量名引用函数,而不是通过函数名。在C语言中,函数名实际上是函数代码的内存地址。因此,函数指针存储着代码段中相应的地址。使用函数指针可以方便地在代码中传递和使用函数作为参数,也可以在程序运行时动态地指定需要调用的函数。如:
- 指针函数
指针函数是返回指针的函数,即本质是一个函数。它允许返回指向指针的指针,也可以返回指针数组。指针函数可以用于动态内存分配、数据结构遍历等场景。此外,它也可以浓缩代码思路,提高代码的可读性和可维护性。如:
注意:函数、指针和数组可以结合一起使用,如下:
1.7.1 结构体
结构体是很实用的数据类型,与数组的区别是,结构体可以定义不同变量类型的成员。声明结构体类型:
结合指针,可以申明一个结构体指针,结构体指针成员变量引用方法是通过“->”符号实现
结构体就是将多个变量组合为一个有机的整体,这样方便对数据的管理。如串口设置:
于是,我们在初始化串口的时候入口参数就可以是 USART_InitTypeDef 类型的变量或者指针变量了,MDK 中是这样做的:
1.7.2 共用体
结构体的定义是需要使用关键字 struct,而共用体则是需要另一个关键字 union来进行定义。共用体只能在定义的时候声明变量,无法在后续的程序中再次声明变量的问题,可以通过 typedef 关键字来自定义数据类型的名称。共用体的定义方式:
注意:共用体变量所占用的内存中仍然只是存储一个数据,空间为最大的数据所占用的空间大小。所以引用不同的类型的时候,会将内存中的数据进行覆盖重写。作用有两个:
①初始化时,可以直接对最长的变量初始化,其他都跟着初始化了
②当一个位置需要存储两种不同类型的数据的时候,可以单独存储(即只用其中一个类型)
③共用体是小端模式
1.7.3 枚举类型
如果变量的值确定,则可以定义枚举类型
注意:
①枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
②第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。在当前值没有赋值的情况下,枚举类型的当前值总是前一个值+1.
TTL和CMOS 的逻辑电平转换:CMOS 电平能驱动 TTL 电平,但 TTL 电平不能驱动 CMOS 电平,需加上拉电阻。
- 进制转换
在学逻辑运算前,首先要知道逻辑运算是对二进制的逻辑运算。而二进制的0he1就代表低电平和高电平,不过由于位数多了二进制太长,不方便阅读,又有了八进制、十进制和十六进制。有关具体进制转换这里不做介绍,一般在数电第一章就介绍,可以用电脑计算器直接计算,如下图所示:
- 逻辑运算作用
1)位与:操作对象&=屏蔽字
可实现目标字段清0,即屏蔽字为0的清零
2)位或:操作对象|=屏蔽字
可实现目标字段置位,即屏蔽字为1的置1
3)异或:操作对象^=屏蔽字
可实现目标字段取反,即屏蔽字为1的取反
@是IAR编译器里指定变量存储地址的一个符号
如设置绝对地址
不是给a赋初值,在内存中存取数据的最小单位一般是字节,但有时存储一个数据不必用一个字节。这是一种位域的结构体,这个结构里a占用的是一个字节中的1位,所以这里的a取值只能是0和1,因为它们都是用1位来表示的。类似bit和_Bool
用volatile申明的变量可以防止被编译器优化而省略
define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。宏定义在编译时,会将目标字符直接替换。常见的格式:
注意:宏定义#define用换行
单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:
它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译,否则编译程序段 2。 其中#else 部分也可以没有
注意:在STM32F407的ADC配置里,这两个等效
- 如何提高代码的可移植性——就是把跟硬件相关的IO都用宏定义来实现
- 在建工程Define里调用的宏定义必须和头文件参数一致,或者修改与硬件一致
- 字长定义
unit8_t= u8是unsigned char(无符号字节,其中unit8_t是标准定义)
unit16_t=u16是unsigned short
unit32_t=u32是unsigned int - 命名
宏定义全部大写字母
函数、数组、变量首字母大写,如果全局变量、数组要用_
局部变量小写字母
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/bcyy/64295.html