×
关于关于关于1. 如何学习单片机7. LED 点阵的学习13.1602 液晶与串口的应用1.1 学习什么单片机7.1 C 语言变量的作用域13.1 通信时序解析1.2 如何学习单片机7.2 C 语言变量的存储类别13.2 1602 整屏移动1.3 单片机学习的准备工作7.3 LED 点阵的介绍13.3 多个 .c 文件的初步认识1.4 单片机开发环境搭建--Keil uVision4安装教程7.4 LED 点阵的图形显示13.4 单片机计算器实例1.5 Keil uVision4 简单使用教程7.5 LED 点阵的纵向移动13.5 串口通信原理和控制程序第一章问题汇总7.6 LED 点阵的横向移动14. I2C 总线与 EEPROM2. 点亮你的 LED 灯8. 单片机按键14.1 单片机 I2C 时序介绍2.1 单片机内部资源8.1 单片机最小系统解析14.2 I2C 寻址模式2.2 单片机最小系统8.2 C 语言函数的调用14.3 单片机 EEPROM 简介2.3 发光二极管(LED 灯)8.3 C 语言函数的形参和实参14.4 EEPROM 单字节读写操作时序2.4 特殊功能寄存器和位定义8.4 单片机按键介绍14.5 EEPROM 多字节读写操作时序2.5 新建一个工程8.5 ​单片机独立按键扫描程序14.6 EEPROM 的页写入2.6 第一个单片机程序8.6 单片机按键消抖程序14.7 I2C 和 EEPROM 的综合编程2.7 将程序下载到单片机8.7 单片机矩阵按键的扫描15. 实时时钟 DS13023. 单片机硬件基础知识学习8.8 简易加法计算器程序15.1 BCD 码介绍3.1 电磁干扰 EMI9. 步进电机与蜂鸣器15.2 单片机 SPI 通信接口3.2 单片机中去耦电容的应用9.1 单片机 IO 口的结构15.3 实时时钟芯片 DS1302 介绍3.3 三极管的的概念及其工作原理9.2 单片机上下拉电阻15.4 DS1302 的硬件信息3.4 单片机中三极管的应用9.3 电机的分类15.5 DS1302 寄存器介绍3.5 74HC138 三八译码器的应用9.4 28BYJ-48 步进电机原理15.6 DS1302 通信时序介绍3.6 LED 灯闪烁程序9.5 让电机转起来15.7 DS1302 的 BURST 模式4. 流水灯的实现9.6 转动精度与深入分析15.8 C 语言复合数据类型4.1 二进制、十进制和十六进制9.7 电机控制程序基础15.9 单片机电子时钟程序设计4.2 C 语言变量类型和范围9.8 实用的电机控制程序16. 红外通信与温度传感器4.3 C 语言基本运算符9.9 单片机蜂鸣器16.1 红外光的基本原理4.4 C 语言 for 循环语句10. 实例练习与经验积累16.2 红外遥控通信原理4.5 C 语言 while 循环语句10.1 单片机数字秒表程序16.3 NEC 协议红外遥控器4.6 C 语言函数的简单介绍10.2 PWM 的原理与控制程序16.4 温度传感器 DS18B204.7 单片机延时方法10.3 单片机交通灯实例17. 模数转换与数模转换4.8 LED 流水灯程序10.4 51单片机 RAM 区域的划分17.1 A/D 和 D/A 的基本概念5. 定时器与数码管基础10.5 单片机长短按键的应用17.2 A/D(模数转换)的主要指标5.1 逻辑电路与逻辑运算11. UART 串口通信17.3 PCF8591 硬件接口5.2 定时器介绍11.1 单片机串行通信介绍17.4 PCF8591 应用程序5.3 定时器的寄存器11.2 RS232 通信接口17.5 A/D 差分输入信号5.4 定时器的应用11.3 USB 转串口通信17.6 D/A 输出5.5 LED 数码管的介绍11.4 IO 口模拟 UART 串口通信17.7 单片机信号发生器程序5.6 数码管的真值表11.5 UART 串口通信的基本应用18. RS485 通信与 Modbus 协议5.7 数码管的静态显示11.6 通信实例与 ASCII 码18.1 RS485 通信6. 中断与数码管动态显示12. 1602 液晶介绍18.2 Modbus 通信协议介绍6.1 C 语言数组12.1 C 语言变量的地址18.3 Modbus 多机通信程序6.2 C 语言 if 语句12.2 C 语言指针变量的声明6.3 C 语言 switch 语句12.3 C 语言指针的简单示例6.4 数码管的动态显示12.4 C 语言指向数组元素的指针6.5 单片机数码管显示消隐12.5 ​C 语言字符数组和字符指针6.6 单片机中断系统12.6 1602 液晶介绍6.7 单片机中断的优先级12.7 1602 液晶的读写时序介绍12.8 1602 液晶指令介绍12.9 1602 液晶简单显示程序

12.5 ​C 语言字符数组和字符指针


常量和符号常量

在程序运行过程中,其值不能被改变的量称之为常量。常量分为不同的类型,有整型常量如1、2、3、100;浮点型常量3.14、0.56、-4.8;字符型常量„a‟、„b‟、„0‟;字符串常量“a”、“abc”、“1234”、“1234abcd”等。

细心的同学会发现,整型和浮点型常量我们直接写的数字,而字符型常量用单引号来表示一个字符,用双引号来表示一个字符串,尤其大家要注意„a‟和“a”是不一样的,这个等会我们要详细介绍。

常量一般有两种表现形式:

  • 直接常量:直接以值的形式表示的常量称之为直接常量。上述举例这些都是直接常量,直接写出来了。
  • 符号常量:用标识符命名的常量称之为符号常量,就是为上面的直接常量再取一个名字。使用符号常量一是方便理解,提高程序可读性,更重要的是方便程序的后续维护,习惯上符号常量我们都用大写字母和下划线来命名。

比如,我们可以把3.14取名为 PI(即π)。再比如,我们上节课的串口程序,我们用的波特率是9600,如果用符号常量来进行提前声明的话,那我们要修改成其它速率的话,就不用在程序中找9600修改了,直接修改声明处就可以了,两种方法举例说明。用 const 声明。比如我们在程序开始位置定义一个符号常量 BAUD。

定义形式是:

    const  类型  符号常量名字=常量值;

    const unsigned int BAUD = 9600;  /*注意结尾有个分号*/

我们就可以在程序中直接把9600改成 BAUD,这样我们如果要改波特率的话,直接在程序开头位置改一下这个值就可以了。用预处理命令 #define 来完成,预处理命令我们先来认识 #define。

定义形式是:

    #define  符号常量名  常量值

    #define  BAUD  9600  /*注意结尾没有分号*/

这样定义以后,只要在程序中出现 BAUD 的话,意思就是完全替代了后边的9600这个数字。

不知大家是否记得,我们之前定义数码管真值表的时候,用了一个 code 关键字。

unsigned char code LedChar[] = {  //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};

我们当时说加了 code 之后,这个真值表的数据只能被使用,不能被改变,如果我们直接写 LedChar[0] = 1;这样就错了。实际上 code 这个关键字是51单片机特有的,如果是其它类型的单片机我们只需要写成 const unsigned char LedChar[]={}就可以了,自动保存到 FLASH 里,而51单片机只用 const 而不加 code 的话,这个数组会保存到 RAM 中,而不会保存到 FLASH 中,鉴于此,在51这个体系下,const 反倒变得不那么重要了,它的作用被 code 取代了,这里大家知道这么回事即可。

我们来对各种类型的常量做进一步说明。

整型常量和浮点型常量就没多少可说的了,之前我们应用的都很熟练了,整型直接写数字就是十进制如128,前边 0x 开头的表示是十六进制 0x80,浮点型直接写带小数点的数据就可以了。

字符型常量是由一对单引号括起来的单个字符。它分为两种形式,一种是普通字符,一种是转义字符。

普通字符就是那些我们可以直接书写直接看到的有形的字符,比如阿拉伯数字0~9,英文字符 A~z,以及标点符号等。它们都是 ASCII 码表中的字符,而它们在单片机中都占用一个字节的空间,其值就是对应的 ASCII 码值。比如„a‟的值是97,„A‟的值是65,„0‟的值是48,如果定义一个变量 unsigned char a = „a‟,那么变量 a 的值就是97。

除了上述这些字符之外,还有一些特殊字符,它们一些是无形的,像回车符、换行符这些都是看不到的,还有一些像‟”这类字符它们已经有特殊用途了,想象一下如果写 '''觉得编译器会怎么去解释呢。针对这些特殊符号,为了可以让它们正常进入到我们的程序代码中,C 语言就规定了转义字符,它是以反斜杠()开头的特定字符序列,让它们来表示这些特殊字符,比如我们用 n 来代表换行。我们用一个简单表格来说明一下常用的转义字符的意思,如表12-2所示。

表 12-2 常用转义字符及含义

字符形式 含义
n 换行
t 横向跳格(相当于 Tab)
v 竖向跳格
b 退格
r 光标移到行首
|反斜杠字符„‟
单引号字符
双引号字符
f 走纸换页
空值

表格不需要大家记住,用到了,过来查就可以了。

字符串常量是用双引号括起来的字符序列,一般我们都称之字符串。如“a”、“1234”、“welcome to www.kingst.org”等都是字符串常量。字符串常量在内存中按顺序逐个存储字符串中的字符的 ASCII 码值,并且特别注意,最后还有一个字符„‟,„‟字符的 ASCII 码值是0,它是字符串结束标志,在写字符串的时候,这个„‟是隐藏的,我们看不到,但是实际却是存在的。所以“a”就比„a‟多了一个 „‟,“a”的就占了2个字节,而 „a‟只占一个字节。

还有一个地方要注意, 就是字符串中的空格, 也是一个字符,比如 “welcome to www.kingst.org ”一共占了26个字节的空间。其中21个字母,2个„.‟,2个 „ ‟(空格字符)以及一个„‟。

字符和字符串数组实例

为了对比字符串、字符数组、常量数组的区别,我们写个了简单的演示程序,定义了4个数组分别是:

unsigned char array1[] = "1-Hello!rn";
unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', 'r', 'n'};
unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};
unsigned char array4[] = "4-Hello!rn";

在串口调试助手下,发送十六进制的1、2、3、4,使用字符形式显示的话,会分别往电脑上送这4个数组中对应的那个数组。我们只是在起始位置做了区分,其它均没有区别。大家可以比较一下效果。

此外还要说明一点,数组1和数组4,数组1我们是发完整的字符串,而数组4我们仅仅发送数组中的字符,没有发结束符号。串口调试助手用字符形式显示是没有区别的,但是大家如果改用十六进制显示,大家会发现数组1比数组4多了一个字节„ ‟的 ASCII 值00。

#include <reg52.h>
bit cmdArrived = 0; //命令到达标志,即接收到上位机下发的命令
unsigned char cmdIndex = 0; //命令索引,即与上位机约定好的数组编号
unsigned char cntTxd = 0; //串口发送计数器
unsigned char *ptrTxd; //串口发送指针

unsigned char array1[] = "1-Hello!rn";
unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', 'r', 'n'};
unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};
unsigned char array4[] = "4-Hello!rn";

void ConfigUART(unsigned int baud);
void main(){
    EA = 1; //开总中断
    ConfigUART(9600); //配置波特率为 9600

    while (1){
        if (cmdArrived){
            cmdArrived = 0;
            switch (cmdIndex){
                case 1:
                    ptrTxd = array1; //数组1的首地址赋值给发送指针
                    cntTxd = sizeof(array1); //数组1的长度赋值给发送计数器
                    TI = 1; //手动方式启动发送中断,处理数据发送
                    break;
                case 2:
                    ptrTxd = array2;
                    cntTxd = sizeof(array2);
                    TI = 1;
                    break;
                case 3:
                    ptrTxd = array3;
                    cntTxd = sizeof(array3);
                    TI = 1;
                    break;
                case 4:
                    ptrTxd = array4;
                    cntTxd = sizeof(array4) - 1; //字符串实际长度为数组长度减1
                    TI = 1;
                    break;
                default:
                    break;
            }
        }
    }
}
/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud){
    SCON = 0x50; //配置串口为模式1
    TMOD &= 0x0F; //清零 T1 的控制位
    TMOD |= 0x20; //配置 T1 为模式2
    TH1 = 256 - (11059200/12/32)/baud; //计算 T1 重载值
    TL1 = TH1; //初值等于重载值
    ET1 = 0; //禁止 T1 中断
    ES = 1; //使能串口中断
    TR1 = 1; //启动 T1
}
/* UART 中断服务函数 */
void InterruptUART() interrupt 4{
    if (RI){ //接收到字节
        RI = 0; //清零接收中断标志位
        cmdIndex = SBUF; //接收到的数据保存到命令索引中
        cmdArrived = 1; //设置命令到达标志
    }
    if (TI){ //字节发送完毕
        TI = 0; //清零发送中断标志位
        if (cntTxd > 0){ //有待发送数据时,继续发送后续字节
            SBUF = *ptrTxd; //发出指针指向的数据
            cntTxd--; //发送计数器递减
            ptrTxd++; //发送指针递增
        }
    }
}

分类导航

关注微信下载离线手册

bootwiki移动版 bootwiki
(群号:472910771)