控制/MCU
1、电平
计算机只能识别0和1,单片机作为计算机的一种,它也只能识别0和1。0和1是用来描述两种状态的,比如,有和无,大和小,胖和瘦,高和矮。
在数字电路里, 我们用两段电压范围来表示0和1 。比如,0V到0.7V表示0,3V到3.3V表示1。在这里,0和1表示的是一个电压范围,因此,0和1称之为电平,0称为低电平,1称之为高电平。这个电压范围是多大,我们在单片机数据手册里可以查询到。
2、进制
数制是人们利用符号进行计数的科学方法。数制有很多种,常用的数制有:二进制,十进制和十六进制。
进位计数制是把数划分为不同的位数,逐位累加,加到一定数量之后,再从零开始,同时向高位进位。进位计数制有三个要素:数码符号、进位规律和计数基数。下表是各常用数制的总体介绍。
关于这三种进制的转换,学习者自行查看相关课本或者百度去学习一下。这里,我只举几个例子:
(137)D = 110^2 + 310^1 + 710^0 = 137; (137)H = 116^2 + 316^1 + 716^0 = 311; (10011010)B = 12^7 + 02^6 + 02^5 + 12^4 + 12^3 + 02^2 +12^1 +02^0 =2^7 + 2^4 +2^3 + 2^1 = 128+16+8+2 = 154
二进制一般不会直接转成十进制,因为太麻烦了。一般是把二进制先转成十六进制,再把十六进制转成十进制,举个例子: (10011010)B = (9A)H = 9*16+10 =154
这里提一下著名的BCD码,又称为8421码。二进制与十六进制转换的思路是,每4位二进制转成1位十六进制。(1111)B=2^3+2^2+2^1+2^0=8+4+2+1=15=F,4位二进制能表示的最大值是15,刚好对应十六进制的最大数字F,所以每4位二进制可以表示1位十六进制。实际上,在单片机编程中,10进制与16进制是用的最多的。
3、逻辑运算符与位运算
&& :逻辑与,同时满足的意思。 比如A&&B,A和B同时为真,那么这个表达式就为真。假用0表示,真即为非0。在单片机编程中,通常用在if语句里来判断几个条件是否同时成立。
& :按位与,每一位进行与运算,对应的位同时为1时,位结果才为1。
比如 1101 0011B & 0110 1101B
1 1 0 1 0 0 1 1 & 0 1 1 0 1 1 0 1 = 0 1 0 0 0 0 0 1
按位与运算常用来判断标志位,比如定义一个8位flag,每一位可以定义为某个特殊的标志位,比如按键长按标志位,长按为1,短按为0。那么按键处理程序就可以通过判断flag的某一位是否为1来判断是否为长按。
|| :逻辑或,是指要么条件A发生,要么条件B发生。多个条件只要有一个条件满足,那么整个表达式就为真。||通常用在if语句里,用来判断某些事件是否发生。
龙虾哥如果在吃饭或者在上班,那么,龙虾哥就不会在群里说话。没有吃饭也没有上班时,就会来群里吹牛。
|: 按位或,是指每一位进行或运算。
在单片机里经常这样使用 A |=0x01; A的最低位置1,其他位不变。
~ :按位取反,取反就是原来是0,那么现在就为1,原来为1现在就为0。
~ 通常和 & 一起使用,用来对某一位进行清0操作。**A &= ~0x01; **A的最低位被清0。
! :非,不是的意思。常用于按键检测判断是不是按下。假设按键按下时,单片机的IO为低电平。则可以这样判断:
<<左移,往左移动的意思。在无符号整型变量中左移一位相当于乘2。
比如,8*2等效8<<1。
>>右移,往右移动的意思。在无符号整型变量中右移一位相当于除2。
比如,8/2等效8>>1。
** <<** 还有一种常见的用法就是,寄存器编程时,用来设置某一位1,使能某位的意思。寄存器R定义为16位,那么,R的第8位置1,我们可以这样写R |= 1<<8;假设第8位是某个功能的使能位,我们可以进一步这样写:
#define ENABLE (1<<8)
#define DISABLE ( ~(1<<8) )
R |= ENABLE;
R &= DISABLE;
通过这种方法,我们就不需要去记忆R的每一位是什么意思了。通过宏定义**#define**把寄存器的每一位取个名字,这样我们使用时,通过名字来赋值,更容易记忆和编写程序,这种思路就是库编程的早期思想,所以,我前面提过不要过于纠结用库还是寄存器编程,其实没啥区别,都是一样的思路。
** >>和<<** 还用于单片机与外设通信里,比如通过io传递8位数据,低位先传输,我们可以这样写。
高位先传输,我们可以写成这样:
4、C语言常用的逻辑结构与多文件编程程序框架
C语言编程的一大特色就是模块化编程,我们通常把单片机外部电路的驱动程序写成模块的形式。这个模块一般由多个函数组成,一个外设驱动程序一般包含一个C文件和一个头文件。
头文件XXXX.H里面编写函数的声明以及一些宏定义,而C文件编写函数的定义。举个例子:
XXXX.H文件 / XXXXX外设驱 *
这里面描述驱动程序的一些说明以及注意事项
编写者,版本号,修改人,日期等信息
*******************************************/
#ifndef __XXXX_H
#define __XXXX_H
#define ON 1
#define OFF 0
#define SET 1
#define RESET 0
void XXXX_WriteData(uint8_t data);
uint8_t XXXX_ReadData(void);
#endif
XXXX.c文件
/******************************************
C文件函数的相关说明
******************************************/
如果一个函数,在其他文件中不使用,只在本驱动文件内部使用的,那么要在这个函数前面加static,比如:
在其他文件中需要调用驱动函数时,只需要加入#include “XXXX.H”这句话就可以了。比如在main.c文件中:
几种常见的逻辑结构
switch case常用于菜单和多条件判断,比如按键检测的处理。每个按键都有对应的按键值,通过按键值的逐一比对,就可以实现按键对应值得处理。
5、变量的生存期与作用域
在函数内部定义的变量叫作局部变量,否则叫作全局变量。
变量的作用域是指变量起作用的范围,在这个范围内,才能使用这个变量。
全局变量的作用域为从变量定义开始到文件结束。当全局变量与局部变量重名时,在函数内部以局部变量为主。
变量的生存期是指,变量存在的时间范围。比如,全局变量定义后,在整个程序执行期间都一直存在。局部变量是在函数内部存在,当函数开始执行时,局部变量开始被创建,当函数执行结束时,局部变量就消失不存在了。
在其他文件中,在全局变量前面加extern,可以拓展全局变量的作用域。
比如,在文件A中: int a=8;在B文件中可以这样写:extern int a;这样子就可以在B文件中使用变量a了。
如何拓展局部变量的生存期?只需要在变量前加static就可以了。为什么要拓展局部变量的生存期呢?因为,我们经常需要局部变量一直存在,但是,我们又不想变量被外部程序改变。因此,我们需要一个一直存在的局部变量。比如,在按键检测中,为了消抖且不长时间堵塞,我们常常写成下面这种形式。
这里循环10000次,相当于200MS滤波消抖,如果直接200MS延迟滤波会耽误其他程序执行。
C语言主要掌握的内容就是数据类型,变量的生存期以及作用域,逻辑运算符,分支语句结构,数组,函数,宏定义,结构体,一维指针。这些内容最好熟悉了后,再学习单片机比较好,这样你看别人程序也能看懂,自己写程序也不会犯错。如果不熟悉这些,那么你自己看别人程序会一知半解,自己写程序也写不好,反而耽误时间以及打击自信心。
全部0条评论
快来发表一下你的评论吧 !