当前位置:首页 > 单片机 > 单片机
[导读]数组不过就是同一类型变量的有序集合。形象的能这样去理解,就像一个 学校在操场上排队,每一个级代表一个数据类型,每一个班级为一个数组,每一个学生就是 数组中的一个数据。数据中的每个数据都能用唯一的下标来确

数组不过就是同一类型变量的有序集合。形象的能这样去理解,就像一个 学校在操场上排队,每一个级代表一个数据类型,每一个班级为一个数组,每一个学生就是 数组中的一个数据。数据中的每个数据都能用唯一的下标来确定其位置,下标能是一维 或多维的。就如在学校的方队中要找一个学生,这个学生在 I 年级 H 班 X 组 Y 号的,那么 能把这个学生看做在 I 类型的 H 数组中(X,Y)下标位置中。数组和普通变量一样,要

求先定义了才能使用,下面是定义一维或多维数组的方式:

“数据类型”是指数组中的各数据单元的类型,每个数组中的数据单元只能是同一数据

类型。“数组名”是整个数组的标识,命名方法和变量命名方法是一样的。在编译时系统会 根据数组大小和类型为变量分配空间,数组名能说就是所分配空间的首地址的标识。“常 量表达式”是表示数组的长度和维数,它必须用“[]”括起,括号里的数不能是变量只能是 常量。

unsigned int xcount [10]; //定义无符号整形数组,有 10 个数据单元

char inputstring [5]; //定义字符形数组,有 5 个数据单元

float outnum [10],[10];//定义浮点型数组,有 100 个数据单元

在 C 语言中数组的下标是从 0 开始的而不是从 1 开始,如一个具有 10 个数据单元的数

组 count,它的下标就是从 count[0]到 count[9],引用单个元素就是数组名加下标,如 count[1] 就是引用 count 数组中的第 2 个元素,如果错用了 count[10]就会有错误出现了。还有一点要 注意的就是在程序中只能逐个引用数组中的元素,不能一次引用整个数组,但是字符型的数 组就能一次引用整个数组。

数组也是能赋初值的。在上面介绍的定义方式只适用于定义在内存 DATA 存储器使 用的内存,有的时候我们需要把一些数据表存放在数组中,通常这些数据是不用在程序中改 变数值的,这个时候就要把这些数据在程序编写时就赋给数组变量。因为 51 芯片的片内 RAM 很有限,通常会把 RAM 分给参与运算的变量或数组,而那些程序中不变数据则应存放在片 内的 CODE 存储区,以节省宝贵的 RAM。赋初值的方式如下:

数据类型 [存储器类型] 数组名 [常量表达式] = {常量表达式};

数据类型 [ 存储器类型] 数组名 [ 常量表达式 1]...... [ 常量表达式 N]={{ 常量表达 式}...{常量表达式 N}};

在定义并为数组赋初值时,开始学习的朋友一般会搞错初值个数和数组长度的关系,而致使 编译出错。初值个数必须小于或等于数组长度,不指定数组长度则会在编译时由实际的初值 个数自动设置。

unsigned char LEDNUM[2]={12,35}; //一维数组赋初值

int Key[2][3]={{1,2,4},{2,2,1}}; //二维数组赋初值

unsigned char IOStr[]={3,5,2,5,3}; //没有指定数组长度,编译器自动设置

unsigned char code skydata[]={0x02,0x34,0x22,0x32,0x21,0x12}; //数据保存在 code 区

下面的一个简单例子是对数组中的数据进行排序,使用的是冒泡法,一来了解数组的使 用,二来掌握基本的排序算法。冒泡排序算法是一种基本的排序算法,它每次顺序取数组中 的两个数,并按需要按其大小排列,在下一次循环中则取下一次的一个数和数组中下一个数 进行排序,直到数组中的数据全部排序完成。

#include

#include

void taxisfun (int taxis2[])

{

unsigned char TempCycA,TempCycB,Temp;

for (TempCycA=0; TempCycA<=8; TempCycA++)

for (TempCycB=0; TempCycB<=8-TempCycA; TempCycB++)

{//TempCycB<8-TempCycA 比用 TempCycB<=8 少用很多循环

if (taxis2[TempCycB+1]>taxis2[TempCycB]) //当后一个数大于前一个 数

{

Temp = taxis2[TempCycB]; //前后 2 数交换

taxis2[TempCycB] = taxis2[TempCycB+1];

taxis2[TempCycB+1] = Temp; //因函数参数是数组名调用形

参的变动影响实参

}

}

}

void main(void)

{

int taxis[] = {113,5,22,12,32,233,1,21,129,3};

char Text1[] = {"source data:"}; //"源数据"

char Text2[] = {"sorted data:"}; //"排序后数据"

unsigned char TempCyc;

SCON = 0x50; //串行口方式 1,允许接收

TMOD = 0x20; //定时器 1 定时方式 2

TCON = 0x40; //设定时器 1 开始计数

TH1 = 0xE8; //11.0592MHz 1200 波特率

TL1 = 0xE8; TI = 1;

TR1 = 1; //启动定时器

printf("%sn",Text1); //字符数组的整体引用

for (TempCyc=0; TempCyc<10; TempCyc++)

printf("%d ",taxis[TempCyc]);

printf("n----------n");

taxisfun (taxis); //以实际参数数组名 taxis 做参数被函数调用

printf("%sn",Text2);

for (TempCyc=0; TempCyc<10; TempCyc++) //调用后 taxis 会被改变

printf("%d ",taxis[TempCyc]);

while(1);

}

例子中能看出,数组同样能作为函数的参数进行传递。数组做参数时是用数组名进行传递的,一个数组的数组名表示该数组的首地址,在用数组名作为函数的调用参数时,它 的传递方式是采用了地址传递,就是将实际参数数组的首地址传递给函数中的形式参数数 组,这个时候实际参数数组和形式参数数组实际上是使用了同一段内存单元,当形式参数数组在 函数体中改变了元素的值,同时也会影响到实际参数数组,因为它们是存放在同一个地址的。 上面的例子同时还使用到字符数组。字符数组中每一个数据都是一个字符,这样一个一 维的字符数组就组成了一个字符串,在 C 语言中字符串是以字符数组来表达处理的。为了 能测定字符串的长度,C 语言中规定以‘o’来做为字符串的结束标识,编译时会自动在字 符串的最后加入一个‘o’,那么要注意的是如果用一个数组要保存一个长度为 10 字节的字 符串则要求这个数组至少能保存 11 个元素。‘o’是转义字符,它的含义是空字符,它的 ASCII 码为 00H,也就是说当每一个字符串都是以数据 00H 结束的,在程序中操作字符数 据组时要注意这一点。字符数组除了能对数组中单个元素进行访问,还能访问整个数组, 其实整个访问字符数组就是把数组名传到函数中,数组名是一个指向数据存放空间的地址指 针,函数根据这个指针和‘/o’就能完整的操作这个字符数组。对于这一段所说的,能 参看下面一例 1602LCD 显示模块的驱动演示例子进行理解。这里要注意就是能用单个字符数组元素来进行运算,但不能用整个数组来做运算,因为数组名是指针而不是数据。

/*============================================================

使用 1602 液晶显示的实验例子

==============================================================

SMC1602A(16*2)模拟口线接线方式 连接线图:

---------------------------------------------------

|LCM-----51 | LCM-----51 | LCM------51 |

---------------------------------------------|

|DB0-----P1.0 | DB4-----P1.4 | RW-------P2.0 |

|DB1-----P1.1 | DB5-----P1.5 | RS-------P2.1 |

|DB2-----P1.2 | DB6-----P1.6 | E--------P2.2 |

|DB3-----P1.3 | DB7-----P1.7 | VLCD 接 1K 电阻到 GND|

---------------------------------------------------

[注:AT89S51 使用 12M 晶体震荡器]

=============================================================*/

#define LCM_RW P2_0 //定义引脚

#define LCM_RS P2_1

#define LCM_E P2_2

#define LCM_Data P1

#define Busy 0x80 //用于检测 LCM 状态字中的 Busy 标识

#include

void WriteDataLCM(unsigned char WDLCM);

void WriteCommandLCM(unsigned char WCLCM,BuysC);

unsigned char ReadDataLCM(void); unsigned char ReadStatusLCM(void); void LCMInit(void);

void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);

void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);

void Delay5Ms(void);

void Delay400Ms(void);

unsigned char code cdle_net[] = {"www.xx.com"};

unsigned char code email[] = {"pnzwzw@xx.com"};

void main(void)

{

Delay400Ms(); //启动等待,等 LCM 讲入工作状态

LCMInit(); //LCM 初始化

Delay5Ms(); //延时片刻(可不要)

DisplayListChar(0, 0, cdle_net); DisplayListChar(0, 1, email); ReadDataLCM();//测试用句无意义 while(1);

}

//写数据

void WriteDataLCM(unsigned char WDLCM)

{

ReadStatusLCM(); //检测忙 LCM_Data = WDLCM; LCM_RS = 1;

LCM_RW = 0;

LCM_E = 0; //若晶体震荡器速度太高能在这后加小的延时

LCM_E = 0; //延时

LCM_E = 1;

}

//写指令

void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC 为 0 时忽略忙检测

{

if (BuysC) ReadStatusLCM(); //根据需要检测忙

LCM_Data = WCLCM; LCM_RS = 0; LCM_RW = 0;

LCM_E = 0;

LCM_E = 0; LCM_E = 1;

}

//读数据

unsigned char ReadDataLCM(void)

{

LCM_RS = 1; LCM_RW = 1; LCM_E = 0; LCM_E = 0; LCM_E = 1; return(LCM_Data);

}

//读状态

unsigned char ReadStatusLCM(void)

{

LCM_Data = 0xFF; LCM_RS = 0; LCM_RW = 1; LCM_E = 0; LCM_E = 0; LCM_E = 1;

while (LCM_Data & Busy); //检测忙信号

return(LCM_Data);

}

void LCMInit(void) //LCM 初始化

{

LCM_Data = 0;

WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号

Delay5Ms(); WriteCommandLCM(0x38,0); Delay5Ms(); WriteCommandLCM(0x38,0); Delay5Ms();

WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号

WriteCommandLCM(0x08,1); //关闭显示 WriteCommandLCM(0x01,1); //显示清屏 WriteCommandLCM(0x06,1); // 显示光标移动设置 WriteCommandLCM(0x0C,1); // 显示开及光标设置

}

//按指定位置显示一个字符

void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)

{

Y &= 0x1;

X &= 0xF; //限制 X 不能大于 15,Y 不能大于 1

if (Y) X |= 0x40; //当要显示第二行时地址码+0x40; X |= 0x80; //算出指令码

WriteCommandLCM(X, 0); //这里不检测忙信号,发送地址码

WriteDataLCM(DData);

}

//按指定位置显示一串字符

void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)

{

unsigned char ListLength;

ListLength = 0; Y &= 0x1;

X &= 0xF; //限制 X 不能大于 15,Y 不能大于 1

while (DData[ListLength]>0x20) //若到达字串尾则退出

{

if (X <= 0xF) //X 坐标应小于 0xF

{

DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符

ListLength++; X++;

}

}

}

//5ms 延时

void Delay5Ms(void)

{

unsigned int TempCyc = 5552;

while(TempCyc--);

}

//400ms 延时

void Delay400Ms(void)

{

unsigned char TempCycA = 5; unsigned int TempCycB; while(TempCycA--)

{

TempCycB=7269;

while(TempCycB--);

};

}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭