0%

计算机基础

  • 原码、反码、补码

    正数的三者相同,负数反码为原码除符号位其余取反,补码为其反码+1

    负数的机器码是其原码的补码

  • 微型计算机配置高速缓冲存储器是为了解决: CPU于内存储器速度不匹配

  • ROM和RAM

    • ROM:只读存储器,在单片机中用于存放程序,配有较大空间,掉电数据不丢失
    • RAM:随机存储器,存放临时数据,使单片机更适用于实时控制系统掉电数据丢失
    • 计算机的内存容量主要指RAM
  • 系统总线:地址总线 AB,控制总线 CB 和双向数据总线 DB。

    • 数据总线(Data Bus―DB)双向、三态:数据总线的根数决定了一次可以传递二进制数的位数
    • 地址总线(Address Bus―AB)单向、三态:地址总线的位数决定了可以直接访问的存储单元(或 I/O 口)的最大可能数 量(即容量)
    • 控制总线(Control Bus―CB):控制总线用来传输控制信号,数据总线和每个元件的数据线相连,为了使 CPU 能够和 其中一个元件正确通信,必须使用三态逻辑元件(特别针对输入元件)。

单片机硬件结构

CPU

分为运算器和控制器

  • 控制器:依次取出ROM中存储单元的指令

    • 程序计数器:PC,16位,指向下一条指令的地址,复位后为0000H
    • 指令寄存器
    • 指令译码器
    • 定时控制电路
  • 运算器ALU:对数据进行算术、逻辑运算

    • 算术逻辑运算部件ALU,负责运算,结果传回ACC,结果的状态传回PSW
    • 累加器ACC
    • 程序状态寄存器PSW
7 6 5 4 3 2 1 0
位名 Cy AC F0 RS1 RS0 OV F1 P
作用 进位/借位 辅助进位 自定义 寄存器选择 寄存器选择 溢出 自定义 奇偶效验
  • Cy:运算结果最高位有无进位/错位
  • AC:运算结果低四位有无向高四位进位
  • F0、F1:用户自定义
  • RS1、RS0:一起决定当前寄存器组(00对应寄存器0组,依次增加)
  • OV:数据是否溢出
  • P:1的个数为奇/偶

时钟电路

  • XTAL1:片内振荡电路输入端
  • XTAL2:输出端

当外部信号源为单片机提供时钟时,1接地,2接信号源

EA

EA(反)为1时CPU从片内读取指令,为0时访问外部

并行IO口

P1

通用输入、输出接口

作输出时:

  • 输出的数据通过内部总线送入锁存器保存

  • 判断数据的每一位。

    为1时,Q=1,Q(反)=0,VT截止,Vcc向引脚输出高电平。

上拉电阻的作用:当VT截止时,使输出强制为1,若没有上拉电阻,输出可能由于噪音上下浮动。

作输入时:

  • 先向锁存器写1(准双向),使VT一直为截止状态。否则VT导通会拉低输入信号。
  • CPU执行读指令时,2导通,数据通过2直接进入内部总线,因此内部总线信号跟随引脚变化。

P2

有两种用途,准双向IO和高8位地址总线

当用作IO时,控制信号使开关接左侧,Q输出1使VT截止。

P3

用户可自行操作第二功能实现相关功能,P3默认为准双向IO

第二功能包括:串口收发、外部中断、定时器输出等

P0

可作为准双向IO,IO时必须外接上拉电阻。第二功能时提供外部存储地址低8位和P2组成16位地址,之后用作数据总线(此时锁存器会保存低八位地址)。

P0具有高阻态

总结:

  • 四个IO均为准双向,在用作输入时都应向锁存器写“1”
  • 除P1外,其余端口都有第二功能:
    • P0:低八位地址和数据总线
    • P2:高八位地址
    • P3:自定义功能
  • P0作为通用IO时需要外接上拉电阻!
  • P0、P2作地址时剩下引脚不能作为IO使用;但是P3各个口独立,未用作第二功能的可以作为IO使用

存储结构

51单片机存储器物理上分为四个空间:

  • 片内程序存储器
  • 片外程序存储器
  • 片内数据存储器
  • 片外数据存储器

从用户使用的逻辑上分为三个区域:

  • 片内外统一编址的程序存储器 0000H-FFFFH(64KB)
  • 片内数据存储 00-FFH 128B
  • 片外数据存储 0000H-FFFFH

程序存储器

程序存储器由ROM和EPROM构成,掉电后数据不丢失

用于存放程序和程序运行时所需的常数。51片内有4K ROM,但地址线为16位,因此做多可以扩展到64KB。当PC值超过内部ROM容量时会自动转向外部寻址。\EA=1时为寻址内部ROM。

MOVC指令用于访问程序存储器。

数据存储器

数据存储器由RAM和专用寄存器组成,掉电后数据丢失

用于存放程序运算的中间结果、标志位、数据缓冲等。51片内有128+128B的数据存储器,可扩充至64KB

内部数据存储器分为低128B和高128B

低128B:RAM区

  • 通用寄存器区,供用户使用,用于临时存放8位数据

    通过修改程序状态寄存器PSW中的RS1和RS0实现选择,00H-1FH 128位

  • 位寻址区,提供地址读写数据

    RAM中的20H-2FH,128位

  • 数据缓存区

特殊功能寄存器SFR

包括51中有特殊功能的寄存器,如ACC、PSW、P0-P3等

不包括PC

时序

时钟周期(振荡周期)

也叫振荡周期,为振荡器频率的倒数,是时序中的最小单位

状态周期

两个振荡周期,一个完成运算,一个完成传送

机器周期

执行一条指令的一个阶段的时间。是单片机的基本操作周期。1个机器周期=6个状态周期=12个振荡周期

指令周期

执行一条指令的周期,通常含有1~4个机器周期。

51单片机分为单周期指令、双周期指令、四周期指令,分别对应n个机器周期。只有乘法和除法是四周砌指令。

因此,晶振周期为12MHz时:

  • 振荡周期=1/12us
  • 机器周期=1us
  • 指令周期:1、2、4us

工作方式

复位方式

  • 上电复位
  • 按键复位

复位后寄存器状态

  • P0~P3:0FFH(全高电平)
  • PC:0000H
  • SP:07H
  • PSW: 00H

51单片机在电复位时RAM保持不变

低功耗模式

  • 休眠模式:只有外部中断工作
  • 空闲模式(待机):只有CPU停止工作

错题

由于RAM片内外访问指令一个是MOV一个是MOVX,因此其地址可以重叠。外部地址总线16位对应64KB,而内部地址可以与其重合,因此RAM最大总量是大于64KB的

51指令系统

51单片机汇编指令查询 | 小董的BLOG (gitee.io)

一般只有A能参与这些逻辑运算

经典

规定

中断

中断请求标志寄存器

定时器/计数器控制寄存器TCON

  • IT 0/1:外部中断触发方式,0低电平触发,1下降沿触发
  • IE 0/1:中断请求标志位,触发中断后硬件自动清零
  • TF 0/1:定时器/计数器溢出中断标志位,触发中断后自动清零
  • TR 0/1:启停定时器

一般就只需设置IT和TR

串行口控制寄存器SCON

  • TI:发送中断标志位,每发送完一帧数据后硬件会对其置1,需用户自行软件清零!
  • RI:接收中断标志位,规则同上。

中断允许寄存器IE

  • EA:总中断允许位
  • ES:串口中断允许位
  • ET:定时器中断允许位
  • EX:外部中断允许位

都是置1允许

若要使用某个中断,必须先开启中断允许位,复位后清零

总结:

  • I开头为TCON寄存器,控制定时器中断触发的方法和标志位
  • RI和TI为SCON寄存器,控制串口相关
  • E开头为中断允许寄存器IE

中断优先级

由中断优先级寄存器IP控制

  • PX:外部中断优先级设置
  • PT:定时器中断优先级设置
  • PS:串口中断优先级设置

中断响应过程

中断请求

硬件完成,请求完成后对应标志位置1

中断查询

CPU检测TCON和SCON中各标志位状态,确定是哪个中断

中断响应

响应条件:

  • EA使能
  • 该中断允许位也使能
  • 有中断请求
  • 无同级或高级中断正在运行

中断处理

中断服务程序

中断返回

复位置位的标志位,重新执行原程序(PC被弹出堆栈)

操作流程

  • 打开对应中断允许位
  • 设置触发方式
  • 开启总中断允许
  • 编写服务函数

定时器/计数器

基本结构

T1、T2分别由两个8位特殊功能寄存器TH和TL组成。当它对外部事件进行计数时,作为计数器使用;当对内部固定频率的机器周期进行计数时,便可以达到定时的目的。

两种工作模式

  • 计数器工作模式

    对外部事件进行计数,计数脉冲来自P3中的对应引脚。

    下降沿触发

    最高频率为振荡频率的1/24,因为识别下降沿需要2个机器周期,即24个振荡周期

  • 定时器工作模式

    计数脉冲为内部时钟脉冲,也就是每个机器周期+1,因此计数频率为振荡频率的1/12,得到的时间:定时器计数值*机器周期时间

当设置好定时器后,定时器就将独立工作,不再占用CPU,只有溢出时才会申请中断。

控制寄存器

工作模式寄存器TMOD

  • GATE:门控位,使用外部中断引脚的高电平来启动定时器(前提是该定时器已被置位运行)
  • C/(/T):1为计数器模式,0为定时器模式
  • M1,M0:同时控制定时器的工作模式,同PSW中的RS用法

控制寄存器TCON

上一节中断中已讲过,控制定时器的开启。

定时器的工作方式

方式0

易得其定时时间为:(2^13 - 初值)*机器周期

计算后的值低5位赋给TL,高8位赋给TH

方式1

16位计数器,其余与方式0一样。

方式2

方式2为8位定时器

  • TL用作定时,TH用于保存初值。当TL溢出后,中断标志置位,同时自动将TH中的值重新装载进TL,无需用户再手动重新装入初值。

更加精准,但计时范围小。

方式3

  • 只有T0能被设置为模式3,T1若为模式3,则停止计数,保存原样。

T0为模式3时

TH和TL分别单独工作

  • TL:使用T0的状态控制位,同方式2,但是溢出后需用户手动装入初值
  • TH:被固定为一个内部定时器,受T1控制位的TR1控制启停,同上占用T1的中断请求TF1

T0为模式3时,T1的设置

T1仍可设置为方式0、1、2,但由于中断被占用,只能将输出送往串口

使用流程

  • 设置模式TMOD
  • 赋初值TL、TH
  • 开启中断ET、EA

  • 开启定时器TR

串口通信

串行通信:数据的各位在同一根数据线上逐位发送、接收。

串行通信按同步方式分为异步通信和同步通信

同步通信

数据传输速率高,但是要求发送时钟和接收时钟严格同步

异步通信

数据组成数据帧方式,收、发端相互独立、互不同步,只需规定收发数据的帧格式即可相互识别。

数据帧格式

  • 起始位:低电平有效
  • 数据位:5~8位
  • 奇偶校验位
  • 停止位:高电平有效

波特率

每秒传输二进制数码的位数,每位的传输时间为1/波特率

51单片机的串口通信

51内部有一个可编程全双工串口。

串口相关寄存器

串行数据缓冲器SBUF

包括发送和接收寄存器,但在逻辑上只有一个SBUF,在使用时可用作发送和接收。(物理上有两个完全独立的SBUF,但是他们地址是一样的)

串行控制寄存器SCON

  • SM0、1工作方式选择位

  • SM2:多机通信控制位

  • REN:允许接收控制位

  • TB8:方式2、3的发送第九位

  • RB8:方式2、3的接收第九位

电源控制寄存器PCON

其中第七位SMOD为1波特率加倍

基础流程

  • 配置SCON
  • 数据送往SBUF
  • 发送、接收数据
  • 中断置位

常用的一些寄存器

片内特殊功能寄存器SFR

大多数特殊寄存器都在这里,但不包括PC!!

程序状态寄存器PSW

用于表示运算结果的状态

7 6 5 4 3 2 1 0
位名 Cy AC F0 RS1 RS0 OV F1 P
作用 进位/借位 辅助进位 自定义 寄存器选择 寄存器选择 溢出 自定义 奇偶效验

中断允许寄存器IE(管理中断允许)E开头

用于管理所有中断

中断优先级寄存器IP(管理中断优先级)P开头

外部中断2位,定时器2位,串口1位,共5位

定时器工作模式寄存器TMOD(管理定时器工作模式)

8位,下4位控制T0,上4位控制T1

定时器中断控制寄存器TCON(管理定时器启动、定时器和外部中断的中断标志位)

其中IT和IE为控制外部中断的位

需要操作的位只有IT和TR,其他都会自动复位

串行控制寄存器SCON(管理串口中断位和串口配置)

电源控制寄存器PCON(波特率翻倍)

仅串口中需使用其第七位翻倍波特率

  • 堆栈的作用:保护现场和断点

  • JB为判断当前位,为1则跳转

如果是A,就是直接寻址,C则为位寻址,位寻址只能操作位

  • C/(/T):0为定时器模式

1.链表的概念

通过多个节点将多个数据元素组成一个链表。

  • 节点:数据元素的存储映像, 由数据域和指针域两部分组成。

优点

  • 数据元素的个数可以自由扩充
  • 插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高

缺点

  • 存储密度小
  • 存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)

2.创建一个单向列表

单向列表即每个节点之间的联系是单向的,仅通过一个指针链接。

创建列表有两种方法:前插法和后插法。

前插法

新的节点链接到上一节点的开头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 typedef struct list
{
int num; //数据域
struct list *last; //指针域
}test;

void setlist(test* linkhead, int data)
{
test *tmp=(test*)malloc(sizeof(test));//分配动态内存,防止生成野指针
tmp->num=data;
tmp->last=linkhead->last;
linkhead->last=tmp;
}

int main()
{
int a[3]={1,2,3};
int i;
test* head=(test*)malloc(sizeof(test));
head->last=NULL;
for(i=0;i<3;++i)
{
setlist(head,a[i]);
}
test* p= head->last;
while(p!=NULL)
{
printf("%d ",p->num);
p=p->next;
}
return 0;
}

前插法创建链表的步骤为(以创建两个节点为例):

1
2
3
4
5
6
7
8
9
10
11
void setlist(test* linkhead, int data)
{
//开辟一个新节点
test *tmp=(test*)malloc(sizeof(test));
//写入需要的数据
tmp->num=data;
//将节点的指针域指向linkhead的指针域
tmp->last=linkhead->last;
//将linkhead的指针域指向新节点首地址
linkhead->last=tmp;
}

也就是说,创建第一个节点时,该节点的指针域先指向NULL后,linkhead指针域再指向该节点首地址。

当刚创建第二个节点时,情况如下:

当执行tmp->last=linkhead->last;时,由于linkhead的指针域此时指向tmp(1)(这里的1指第一块节点的内存空间)的首地址,所以tmp(2)的指针域会通过linkhead直接也指向tmp(1)的首地址:

这样之后,两个节点就通过指针链接在了一起,且tmp(2)在tmp(1)之前。

由此可以看出,前插法的原理就是以linkhead为媒介来链接节点,linkhead本身是不存储数据的

最后,执行linkhead->last=tmp;将linkhead指针域指向第二个节点首地址,以供第三个节点的指针域找到第二个节点。

以此类推,假如现在创建了4个节点,那情况就应该像这样:

在例程中,由于前插法得到的链表顺序为最后输入的数据开始,所以最后打印的结果为3 2 1

尾插法

相对于前插法,尾插法使用起来逻辑会更加清晰,因为其链接顺序是在每一个节点后,所以尾插法要更容易理解一些。

结构体复习 | 小董的BLOG (gitee.io)中讲到的列表就是使用的尾插法,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
typedef struct list
{
int num;
char name[30];
struct list *next;
}test;

test *creat()
{
test *linkhead,*linkend,*tmp;
int i;
for(i=0;i<3;i++)
{
tmp=(test*)malloc(sizeof(test));//分配动态内存
scanf("%d %s",&tmp->num,tmp->name);
if(i==0)
{
linkhead=tmp;//第一次先将当前节点赋给链头链尾
linkend=tmp;
}
else//若不是第一次,先将当前节点存入上一个节点的next中,再将linkend更新为当前节点以供下一次访问
{ //当前节点的next
linkend->next=tmp;//通过linkend直接指向内存中的一个struct list变量使其指向tmp的内存
linkend=tmp;
//以第二个节点为例,先将第linkend(也就是tmp(1))的指针域指向二个节点首地址
//再将linkend指向tmp(2),以供后面的节点重复23、24行的操作
}
}
linkend->next=NULL;
return linkhead;
}

如果说前插法是通过linkhead为媒介进行链接,那尾插法就是通过linkend为媒介链接各节点。

3.双向链表的创建

同样分为使用前插法和后插法,但是尾插法会简单很多,所以先讲一下尾插法

尾插法创建双向链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 typedef struct list
{
int num;
char name[30];
struct list *last; //存放上一节点
struct list *next; //存放下一节点
}test;

test *creat()
{
test *linkhead,*linkend,*tmp;
int i;
for(i=0;i<3;i++)
{
tmp=(test*)malloc(sizeof(test));
scanf("%d %s",&tmp->num,tmp->name);
if(i==0)
{
linkhead=tmp;
linkend=tmp;
linkhead->last=NULL; //因为链头没有上一节点,固其last指针域指向NULL
}
else
{
tmp->last=linkend; //把上一节点地址存入本节点的last指针中
linkend->next=tmp;
linkend=tmp;
}
}
linkend->next=NULL;
return linkhead;
}

尾插法由于其创建链表的方式为顺序创建,因此只需在原有的基础上添加存放上一节点的指针就行了

使用双向链表会增加内存,但是就可以通过任意节点找到其他节点了。

使用前插法创建双向链表

使用前插法的话会稍微麻烦一些,而且理解起来也会有点绕,感觉用尾插法好一些。

(这是我自己写的方法,感觉有点绕)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 typedef struct list
{
int num; //数据域
struct list *last;
struct list *next;
}test;

int time=1;
test* InsertList(test *head,int val)
{
test *tmp,*end;
tmp=(test *)malloc(sizeof (test));
if(time==1) //建立第一个节点时将该节点作为链尾
{
tmp->num=val;
tmp->last=head->last;
head->last=tmp;
end=tmp; //end作为链尾将一直指向第一个建立的节点,后续不再改变end
}
else
{
tmp->num=val;
tmp->last=head->last;
head->last->next=tmp; //将上一个节点的next指针指向当前节点(有点套娃的感觉)
head->last=tmp;
tmp->next=NULL; //每个节点的next先清零,为了让最后一个节点指向null
}
++time;
return end;
}

int main()
{
int a[3]={1,2,3};
int i;
test *linkhead, *linkend;
linkhead=(test *)malloc(sizeof (test));
linkhead->last=NULL;
for(i=2;i>=0;--i) //由于是前插法,所以输入倒序输入
{
linkend=InsertList(linkhead,a[i]);
}
test *p1,*p2;
p1=linkhead->last;
p2=linkend;
while(p1!=NULL)
{
printf("%d\n",p1->num);
p1=p1->last;
}
while(p2!=NULL)
{
printf("%d\n",p2->num);
p2=p2->next;
}
system("pause");
return 0;
}

在写这个双向链表时我遇见了个问题:对于linkend变量,我在主函数中定义后,像通过像linkhead一样的方式对linkend进行改变,但是始终无法实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void InsertList(test *head,test *end;int val)
{
test *tmp,*end;
tmp=(test *)malloc(sizeof (test));
if(time==1)
{
tmp->num=val;
tmp->last=head->last;
head->last=tmp;
end=tmp;
}
else
{
tmp->num=val;
tmp->last=head->last;
head->last->next=tmp;
head->last=tmp;
tmp->next=NULL;
}
++time;
}

int main()
{
test *linkhead, *linkend;
linkhead=(test *)malloc(sizeof (test));
linkhead->last=NULL;
...
}

如果在主函数中没有对linkend分配内存,linkend将成为一个野指针,无法指向字符值常量。

但是如果分配了内存,由于linkhead是通过last成员来寻找链表,但是linkend却无法像linkhead一样正常实现功能,在经过整整一天的查找资料&调试后,终于发现了原因:

当一个函数的形参是结构体指针时,这个形参本身的地址(不是所指向的地址)和实参的地址(也不是所指向的地址)是不同的。用例程举例:

  • &linkend≠&end,因此,在函数中改变形参end所指向的内容是完全不会影响实参linkend的,因为此时这两位可以理解成两个变量

那这时大家肯定会有个疑惑:那linkhead为什么就可以实现想要链表功能呢?

我们仔细看InsertList函数中的内容,你肯定会发现,函数中没有出现直接操作linkhead的操作,只有操作linkhead成员的操作,这里就引出了一个折磨了我半天的知识点

  • 对于一个结构体指针形参,形参名的地址和实参名的地址是不能划等号的,但是形参成员的地址和实参成员的地址是完全相同的,用例程举个例子

    1
    2
    &linkend≠&end
    linkend->next=end->next (=是等于的意思,不是赋值的意思)

其实这里的解释有点类似于在结构体复习 | 小董的BLOG (gitee.io)最后我对一重指针的解释:

  • 假如有两个并列的指针指向同一个变量,你不能通过改变其中一个指针的指向来影响另一个指针的指向

那么这时又引出了一个问题,形参end和实参linkend指向哪里?

先说一下结论:linkend和end都指向linkend的第一个成员num的地址。

也就是说,形参end和实参linkend作为两个不同的指针变量,他们指向了同一个地址(也许可以通过这一特性来写一个链表?但是我不想写了😋)

其实这个知识点符合我们对形参和指针变量的认知,所以这里比较重要的点其实是:

  • 不同于数组,结构体指针名地址≠第一个成员的地址,但指针名指向第一个成员的地址,可以理解为,结构体指针名指向一块内存,该内存是由若干个结构体成员组成的,这些成员的类型在定义结构体时确定。

这也加深了我对end=(LNode*)malloc(sizeof (LNode));的理解,其实就是一个指针指向了一块内存,但是可以通过结构体的方式访问这块内存中的成员

总结

  • 当一个函数的参数是结构体指针时,操作这个函数的形参名不会影响实参,同时这个形参名的作用域只限于函数体;但是操作这个形参的任何成员等同于直接操作实参的对应成员,因为它们本身的地址是相同的
  • 结构体指针名指向第一个成员的地址

所以,结构体指针并不一定要指向一个明确的结构体,当他指向一块内存时,可以通过直接赋值结构体指针成员的方式操作这块内存(这块内存其实就是一个隐式的,没有具体名字的结构体)

一些感想:好久没有遇到过这么折磨的问题了,一开始完全没有头绪,网络上对于这方面的讲解也是几乎没有,还好找到了这篇博客启发了我C语言理解指针作为形参作用(博客中用的C++,但是原理是完全相同的)

内容大部分来自MATLAB介绍_w3cschool只是cv一下方便自己查阅

基础内容

常用运算符和特殊字符

运算符 目的
+ 加;加法运算符
- 减;减法运算符
* 标量和矩阵乘法运算符
.* 数组乘法运算符
^ 标量和矩阵求幂运算符
.^ 数组求幂运算符
\ 矩阵左除
/ 矩阵右除
.\ 阵列左除
./ 阵列右除
: 向量生成;子阵提取
( ) 下标运算;参数定义
[ ] 矩阵生成
. 点乘运算,常与其他运算符联合使用
续行标志;行连续运算符
, 分行符(该行结果不显示)
; 语句结束;分行符(该行结果显示)
% 注释标志
_ 引用符号和转置运算符
._ 非共轭转置运算符
= 赋值运算符

特殊变量和常量

Name Meaning
ans 默认的变量名,以应答最近依次操作运算结果
eps 浮点数的相对误差
i,j 虚数单位,定义为 i2 = j2 = -1
Inf 代表无穷大
NaN 代表不定值(不是数字)
pi 圆周率

matlab命名变量

  • 名称为字母开头,后可接任意数量的下划线、字母、数字
  • 区分大小写

保存工作进度

MATLAB使用save命令保存工作区中的所有变量,然后作为一个扩展名为.mat的文件,在当前目录中。

1
2
save myfile
load myfile

变量

显示已使用的变量

1
2
3
who
whos
clear %删除所有(或指定)的变量%

延伸行

  • 使用...将当前行延伸至下一行
1
2
3
4
5
initial_velocity = 0;
acceleration = 9.8;
time = 20;
final_velocity = initial_velocity ...
+ acceleration * time

格式命令

命令 作用
format short 显示4位小数
format long 显示16位小数
format bank 显示两位小数
format rat 显示有理表达式
format + 正数
  • 在命令后加e则会显示对应的指数形式

创建矩阵

1
m = [1 2 3; 4 5 6; 7 8 9]

结果:

1
2
3
4
5
m =

1 2 3
4 5 6
7 8 9

同理可创建行、列向量。

相关命令

向量、矩阵相关

命令 作用/目的
cat 连接数组
find 查找非零元素的索引
length 计算元素数量
linspace 创建间隔向量
logspace 创建对数间隔向量
max 返回最大元素
min 返回最小元素
prod 计算数组元素的连乘积
reshape 重新调整矩阵的行数、列数、维数
size 计算数组大小
sort 排序每个列
sum 每列相加
eye 创建一个单位矩阵
ones 生成全1矩阵
zeros 生成零矩阵
cross 计算矩阵交叉乘积
dot 计算矩阵点积
det 计算数组的行列式
inv 计算矩阵的逆
pinv 计算矩阵的伪逆
rank 计算矩阵的秩
rref 将矩阵化成行最简形
cell 创建单元数组
celldisp 显示单元数组
cellplot 显示单元数组的图形表示
num2cell 将数值阵列转化为异质阵列
deal 匹配输入和输出列表
iscell 判断是否为元胞类型

绘图

命令 作用/目的
axis 人工选择坐标轴尺寸
fplot 智能绘图功能
grid 显示网格线
plot 生成XY图
print 打印或绘图到文件
title 把文字置于顶部
xlabel 将文本标签添加到x轴
ylabel 将文本标签添加到y轴
axes 创建轴对象
close 关闭当前的绘图
close all 关闭所有绘图
figure 打开一个新的图形窗口
gtext 通过鼠标在指定位置放注文
hold 保持当前图形
legend 鼠标放置图例
refresh 重新绘制当前图形窗口
set 指定对象的属性,如轴
subplot 在子窗口中创建图
text 在图上做标记
bar 创建条形图
loglog 创建双对数图
polar 创建极坐标图像
semilogx 创建半对数图(对数横坐标)
semilogy 创建半对数图(对数纵坐标)
stairs 创建阶梯图
stem 创建针状图

数据类型

常用的数据类型

数据类型 描述
int8 8位有符号整数
uint8 8位无符号整数
int16 16位有符号整数
uint16 16位无符号整数
int32 32位有符号整数
uint32 32位无符号整数
int64 64位有符号整数
uint64 64位无符号整数
single 单精度数值数据
double 双精度数值数据
logical 逻辑值为10,分别代表truefalse
char 字符数据(字符串作为字符向量存储)
  • 但是用法和C不同,matlab中用法类似于函数(matlab的变量不用任何定义语句)

    1
    2
    3
    4
    5
    6
    str = 'Hello World!'
    n = 2345
    d = double(n)
    un = uint32(789.50)
    rn = 5678.92347
    c = int32(rn)

数据类型确认函数

函数 描述说明
is 检测状态
isa 确定输入是否是指定类的对象
iscell 确定输入是单元格数组
iscellstr 确定输入是字符串的单元格数组
ischar 确定项目是否是字符数组
isfield 确定输入是否是结构数组字段
isfloat 确定输入是否为浮点数组
ishghandle 确定是否用于处理图形对象句柄
isinteger 确定输入是否为整数数组
isjava 确定输入是否为Java对象
islogical 确定输入是否为逻辑数组
isnumeric 确定输入是否是数字数组
isobject 确定输入是否为MATLAB对象
isreal 检查输入是否为实数数组
isscalar 确定输入是否为标量
isstr 确定输入是否是字符数组
isstruct 确定输入是否是结构数组
isvector 确定输入是否为向量
class 确定对象的类
validateattributes 检查数组的有效性
whos 在工作区中列出变量,其大小和类型

运算符

位运算

函数 目的/作用
bitand(a, b) 当a、b是一个或数个无符号整数或无符号整数数组,返回参数a和b位和,
bitcmp(a) a的补码
bitget(a,pos) 在指定位置pos中获取位,在整数数组a中
bitor(a, b) 对数a和b按位或
bitset(a, pos) a的集合点在一个特定的位置pos
bitshift(a, k) 返回一个移到左K位,相当于乘以2K。K负值对应的位权转移或除以2\ K\ 向负无穷舍入到最近的整数。任何溢出位都被截断。
bitxor(a, b) 对数a和b按位异或
swapbytes 交换字节顺序

集合运算

函数 描述
intersect(A,B) 设置两个数组的交集;返回A和B所共有的值。返回的值按排序顺序排列。
intersect(A,B,’rows’) 将A和B的每一行作为单个实体处理,并返回A和B的公共行。返回的矩阵的行按排序顺序排列。
ismember(A,B) 返回与A大小相同的数组,包含1(true),其中A的元素在其他地方的B中找到,它返回0(false)。
ismember(A,B,’rows’) 将A和B的每一行作为单个实体处理,并返回一个包含1(true)的向量,其中矩阵A的行也是B的行;否则,它返回0(false)。
issorted(A) 如果A的元素按排序顺序返回逻辑1(true),否则返回逻辑0(false)。输入A可以是向量,也可以是N-by-1或1-by-N的字符串数组。如果A和sort(A)的输出相等,则A被认为是排序的。
issorted(A, ‘rows’) 如果二维矩阵A的行按排序顺序返回逻辑1(真),否则返回逻辑0(假)。 如果A和排序(A)的输出相等,则认为矩阵A被排序。
setdiff(A,B) 设置两个数组的差值;返回不在B中的值。返回数组中的值按排序顺序排列。
setdiff(A,B,’rows’) 将每一行A和B行作为单个实体处理,并返回一个不在B中的行。返回的矩阵的行按排序顺序排列。“行”选项不支持单元格数组。
setxor 设置两个数组的异或
union 设置两个数组的并集
unique 数组中唯一的值

语句

条件判断

1
2
3
4
if (...)
.....;
.....;
end
1
2
3
4
5
6
7
switch(...)
case .. %无冒号
....;
....;
case ..
.... %单行可以不加分号
end
  • 其他用法与C基本相同
  • end后不须跟分号(即使后面还有语句)

循环

1
2
3
4
while()
...;
...;
end

for循环

for循环有三种用法:

  • initval:endval 默认步长为+1,递增

    1
    2
    3
    4
    for a = 10:20 
    ...;
    ...;
    end
  • initval:step:endval step可自定义步长

    1
    2
    3
    4
    for a = 1.0: -0.1: 0.0
    ...;
    ...;
    end
  • 索引数组

    1
    2
    3
    4
    for a = [24,18,17,23,28]
    ...;
    ...;
    end

向量

元素索引

1
2
3
rv = [1 2 3 4 5 6 7 8 9];
sub_rv = rv(3:7)
rv(1)

向量转置

1
2
r = [ 1 2 3 4 ];
tr = r';

求向量的模

1
2
3
4
v = [1: 2: 20];
sv = v.* v;
dp = sum(sv);
mag = sqrt(dp);

向量点积

若要求取向量的点积,不能直接使用运算符求得,需使用函数dot(a,b)

1
2
3
v1 = [2 3 4];
v2 = [1 2 3];
dp = dot(v1, v2);
  • *号用于运算矩阵乘法,而.*用于数组的乘法(数组的乘法有一套自己的算法)

矩阵

矩阵索引

  • 第m行,第n列的一个元素

    1
    mx(m,n);
  • 第m列的所有元素

    1
    2
    a = [ 1 2 3 4 5; 2 3 4 5 6; 3 4 5 6 7; 4 5 6 7 8];
    v = a(:,4) %第4列的所有元素,并返回一个列向量
    1
    2
    a = [ 1 2 3 4 5; 2 3 4 5 6; 3 4 5 6 7; 4 5 6 7 8];
    a(:, 2:3) %第2-3列的所有元素

注:索引从1开始,而不是0

删除行或列

1
2
a = [ 1 2 3 4 5; 2 3 4 5 6; 3 4 5 6 7; 4 5 6 7 8];
a( 4 , : ) = []

除法

1
2
3
4
a = [ 1 2 3 ; 4 5 6; 7 8 9];
b = [ 7 5 6 ; 2 0 8; 5 7 1];
c = a / b %a右乘b的逆矩阵(inv(b))
d = a \ b %b左乘a的逆矩阵
  • 必须为方阵,因为只有方阵才有逆矩阵

行列式

1
2
a = [ 1 2 3; 2 3 4; 1 2 5]
det(a)
  • 行列式一定为方阵
  • 转置不改变行列式的值

多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
a = [7 9 5; 6 1 9; 4 3 2];
a(:, :, 2)= [ 1 2 3; 4 5 6; 7 8 9];

结果:
a(:,:,1) =
7 9 5
6 1 9
4 3 2

a(:,:,2) =
1 2 3
4 5 6
7 8 9

或使用cat函数:

1
b = cat(3,a,[ 1 2 3; 4 5 6; 7 8 9]); %维度+要串联的阵列

单元阵列

单元阵列的阵列中每个单元格可以存储不同的维度和数据类型的数组的索引单元格

1
C = cell(dim1,...,dimN) %dim 是一个标量整数或整数向量,指定单元格阵列C的尺寸
1
2
3
4
5
6
7
c = cell(2, 5);
c = {'Red', 'Blue', 'Green', 'Yellow', 'White'; 1 2 3 4 5};

结果:
c =
'Red' 'Blue' 'Green' 'Yellow' 'White'
[ 1] [ 2] [ 3] [ 4] [ 5]

单元阵列索引

可以使用大括号或小括号索引,索引方法与前相同

函数

一般函数格式

1
2
3
4
function [out1,out2,...] = fun_name(in1,in2,...)
%帮助文本
...
end

匿名函数

1
2
power = @(x, n) x.^n;
result1 = power(7, 3)

绘图

1
plot(x, y), xlabel('x'), ylabel('Sin(x)'), title('Sin(x) Graph'),

绘制多条曲线

1
2
3
4
5
x = [-10 : 0.01: 10];
y = 3*x.^4 + 2 * x.^3 + 7 * x.^2 + 2 * x + 9;
g = 5 * x.^3 + 9 * x + 2;
plot(x, y, 'r', x, g, 'g') % 'r','g'代表曲线颜色
legend('y(x)','g(x)');

设置轴刻度

1
axis ( [xmin xmax ymin ymax] )

绘制多个图像

1
subplot(a,b,c);
  • a:总行
  • b:总列
  • c:第几图

例如生成2*2个图像,第二行第一个图则为subplot(2,2,3);

代数

声明一个代数

1
syms x 

解代数方程

1
solve(x^2 -7*x + 12 == 0);

或:

1
2
eq='x^2 -7*x + 12 == 0';
result=solve(eq);

若要单独显示各个根:

1
2
3
4
disp(result(1));
disp(result(2));
disp(result(3));
disp(result(4));
  • 我的matlab必须手动打印结果,不知道为什么

多项式

当计算一个一元多次项时,可以使用行向量表示,例如:

方程 P(x) = x^4 + 7x^3 - 5x + 9 可以表示为:

1
p = [1 7 0 -5 9];

求解:

1
polyval(p,4) %计算当x=4时多项式的值

还可以通过输入矩阵的方式计算多个值:

1
2
3
4
5
6
7
8
9
10
p = [1 7 0  -5 9];
X = [1 2 -3 4; 2 -5 6 3; 3 1 0 2; 5 -7 3 8];
polyval(p, X);

%结果:
ans =
2307 -1769 -939 4499
2314 -2376 -249 4695
2256 -1892 -549 4310
4570 -4532 -1062 9269

使用多项式的曲线拟合

当我们要对离散点拟合一条曲线时,可以使用polyfit函数,该函数的作用是通过最小二乘法的方式拟合出一条曲线的多项式

1
2
x = [1 2 3 4 5 6]; y = [5.5 43.1 128 290.7 498.4 978.67]; 
p = polyfit(x,y,5)

此时p为一个多项式,可以使用polyval在坐标集上拟合出该曲线

1
2
3
x2 = 1:0.1:6;          
y2 = polyval(p,x2);
plot(x,y,'o',x2,y2)