jackduan
发表于 2025-2-6 16:55:18
第二章 C语言基础
之前看到这章总是略过,但是现在认真看起来还是相当费劲的。之前看书的浮浮燥燥,导致之后学习运用中的磕磕绊绊。
年过完了,过年之前看的这章内容,年前间断的看了两三天,年后看之前看过的内容竟然还是有些生疏,而且到现在这章内容还没看完。
现在看到2.4.7函数作用域,还没开始。
刚才看了一下函数调用,重点看传值调用和传地址调用。
总结了一下就是:传值调用,调用结束后主调函数参数都不会发生变化;传地址调用,调用结束后主调函数的实参也会发生变化。
其中数组调用中,调用数组中的元素属于传值调用;当用数组名作为实参时,是将数组所在内存单元中的首地址传递给函数,而不是将整个数组元素复制到被调函数中去,是传地址调用,所以调用结束后主调函数的实参会发生变化。
jackduan
发表于 2025-2-7 16:20:24
第二章 进行完毕
第二章终于告一段落,不敢说结束或者完毕,只能是暂时看完了。
函数作用域:C51将所有函数都认为是全局性的(这里的C51我理解为KEIL编译软件C51部分),可以被同一个项目中的另一个文件的任何函数所调用,但另一个文件调用该函数前,应在文件的开头(即所有函数外的最上端)声明被调函数。
请原谅我在做内容总结的时候,照书全搬,我真的觉得这书编辑的很好,没有一句话甚至没有一个字是需要省略或改动。
头文件的编写:一般格式如下:
#ifndef<标识>
#define<标识>
头文件代码块
#endif
<标识>通常使用头文件名并且全部大写,前后各加两个短下划线,并把文件名中的“.”变成一个下划线。
例如:ifndef __STDIO_H__//__ 是两个下划线
jackduan
发表于 2025-2-7 17:14:42
第三章定时器计数器
同一个模块当用于内部系统时钟计数时称为定时器,当用于外部输入脉冲计数时称为计数器。
核心是一个加1计数器。
我们使用单片机内部功能模块就是要把这些开关合上或者断开,这些开关的合上或者断开是由特殊功能寄存器控制的,因此控制开关实际是控制特殊功能寄存器。
jackduan
发表于 2025-2-8 15:21:59
3.1.2定时器/计数器的相关寄存器
凡是地址能被8整除的寄存器都可以进行位寻址,即直接对位进行操作,地址不能被8整除的寄存器只能对整个字节进行操作。
TMOD定时器方式寄存器:C/T=0;用于内部定时,设为1用于外部计数。
M1M0,定时器工作方式:00,16位自动重装; 01,16位定时计数;10,8位自动重装定时计数;11:
不可屏蔽中断的16位重装定时器。
TCON定时器计数器控制寄存器:TF1、TF0溢出标志位,计数器溢出时自动置1,进入相应中断后由硬件清零,若没编写中断函数
则必须由软件清零。TR1、TR0:运行控制位,置1启动定时器,置0关闭定时器。
AUXR辅助寄存器:用来设置T0、T1的速度和定时器T2的功能以及串口UART的波特率控制。
T0x12、 T1x12速度控制位:0,传统8051单片机的速度即12分频;1,传统8051速度的12倍,即不分频。
如果串口1用T1作为波特率发生器,T1x12决定串口1是12T还是1T。
jackduan
发表于 2025-2-11 11:29:08
3.1.3 定时器工作方式
工作方式0:16位自动重装
T1可以做串口波特率发生器和时钟输出;T0不能做串口波特率。
工作方式1:16位定时器
本模式不能用于时钟输出。
工作方式2:8位自动重装
T2 T3 T4固定为16位自动重装方式。
3.1.4初值计算
M1M0=00 方式0 初值=65536 - 待计数 16位
M1M0=01 方式1 初值=65536 - 待计数 16位
M1M0=10 方式2 初值=256 - 待计数 8位
初值计算:
单个定时脉冲周期:Tin=1/fin
待计数值=定时时间/单个定时脉冲周期=T/Tin=fin*T
M1M0=00 方式0 初值=65536 - fin*T=65536-SYSclk*T
3.2 可编程时钟输出
MCLKO/P5.4的时钟输出频率设置不要大于IO口追高允许频率13.5MHZ,否则不能正常输出。时钟输出时IO口都会切换到推挽输出状态,要注意输出信号不能短路。
3.3中断系统
单片机暂停当前程序去执行其他程序的过程就成为中断。
中断允许寄存器IE。EA:总开关。EA=1开总中断,EA=0关总中断。
中断函数格式:void UART1(void)interrupt4{}串口1中断函数
注意事项:
1.只要程序中开启了中断,就必须编写对应的中断函数,哪怕是空函数也必须有(空函数执行RETI中断返回指令),否则中断产生时找不到克执行的中断函数,会引起程序功能错乱或死机。
2.任何函数都不能直接调用中断函数,另外中断函数可放在程序中任何位置而不需要声明,只要产生中断,程序就能自动跳入中断函数执行。
单片机相应中断后,不会自动关闭中断系统。如果用户程序不希望出现中断嵌套,则必须在中断服务程序的开始处关闭中断,禁止更高优先级的中断请求中断当前中断服务程序。
在中断程序即将退出时必须清除相应的中断标志。
查询方式就是
设置好定时器
for(;;)
{
if(TF0)
{
TF0=0;
P0_0=!P0_0;
}
}
中断方式就是
也要设置好定时器;
EA=1;//开总中断
ET0=1;//开定时器中断
while(1);
}
void Timer0() inerrupt 1
{
P0_0=!P0_0;
}
jackduan
发表于 2025-2-11 11:29:10
3.1.3 定时器工作方式
工作方式0:16位自动重装
T1可以做串口波特率发生器和时钟输出;T0不能做串口波特率。
工作方式1:16位定时器
本模式不能用于时钟输出。
工作方式2:8位自动重装
T2 T3 T4固定为16位自动重装方式。
3.1.4初值计算
M1M0=00 方式0 初值=65536 - 待计数 16位
M1M0=01 方式1 初值=65536 - 待计数 16位
M1M0=10 方式2 初值=256 - 待计数 8位
初值计算:
单个定时脉冲周期:Tin=1/fin
待计数值=定时时间/单个定时脉冲周期=T/Tin=fin*T
M1M0=00 方式0 初值=65536 - fin*T=65536-SYSclk*T
3.2 可编程时钟输出
MCLKO/P5.4的时钟输出频率设置不要大于IO口追高允许频率13.5MHZ,否则不能正常输出。时钟输出时IO口都会切换到推挽输出状态,要注意输出信号不能短路。
3.3中断系统
单片机暂停当前程序去执行其他程序的过程就成为中断。
中断允许寄存器IE。EA:总开关。EA=1开总中断,EA=0关总中断。
中断函数格式:void UART1(void)interrupt4{}串口1中断函数
注意事项:
1.只要程序中开启了中断,就必须编写对应的中断函数,哪怕是空函数也必须有(空函数执行RETI中断返回指令),否则中断产生时找不到克执行的中断函数,会引起程序功能错乱或死机。
2.任何函数都不能直接调用中断函数,另外中断函数可放在程序中任何位置而不需要声明,只要产生中断,程序就能自动跳入中断函数执行。
单片机相应中断后,不会自动关闭中断系统。如果用户程序不希望出现中断嵌套,则必须在中断服务程序的开始处关闭中断,禁止更高优先级的中断请求中断当前中断服务程序。
在中断程序即将退出时必须清除相应的中断标志。
查询方式就是
设置好定时器
for(;;)
{
if(TF0)
{
TF0=0;
P0_0=!P0_0;
}
}
中断方式就是
也要设置好定时器;
EA=1;//开总中断
ET0=1;//开定时器中断
while(1);
}
void Timer0() inerrupt 1
{
P0_0=!P0_0;
}
jackduan
发表于 2025-2-11 12:15:42
4 串口通信
传输数据的过程称为通信。
TXD/P3.1RXD/P3.0
计算机主板出来的RS232信号电平是+—9V。
RS232要求2条信号线平行排列,不要绞合。
当单片机执行一条写SBUF的指令时,就启动串行通信的发送,数据由串行发送端TXD输出。
SBUF=dat;
一个串行帧:1位起始位低电平;8位数据位,低位在前,高位在后;第9位奇偶校验位;停止位高电平。
只要单片机允许接收REN=1,一旦检测到RXD引脚电压从1到0跳变,就启动接收。
单位bps 位/秒
例如波特率为9600比特,帧格式为N.8.1(10位),每秒传送字节为:9600/10=960个。
字节中每一位传送的时间为波特率的倒数:T=1/9600=104us
帧与帧之间的间隙用高电平1填补。
SCON 串口控制寄存器
REN 确定是否允许串口接收数据: REN=1允许接收,REN=0禁止接收。
TI :一帧结束后由硬件产生的中断请求标志,只能通过软件清零;
RI :接收中断标志,串口接收电路自动接收完一帧数据后由硬件置1,只能通过软件清0。
数据缓冲寄存器SBUF
发送缓冲区用于存放即将发送输出的数据,接收缓冲区存放串口自动给接收到的外部数据,他们在硬件上是相互独立的。
由于发送缓冲区只能写入不能读出,接收缓冲区只能读出而不能写入,因而两个缓冲区可以公用一个地址,SBUF地址为99H。
只要把数据送进SBUF,就永远不可能再用读SBUF的方法得到这个数,读出来的是接收SBUF中的数。
应该再下一帧数据由移位寄存器接收完毕之前将SBUF中的数据取走,否则会覆盖前一帧数据。
例如波特率115200,帧格式10位,每秒传送字节数为115200/10=11520,一个字节需要的时间是1/11520=86.8us,这就要求接收到串口中断申请(RI=1)后的86.8us内取走SBUF中的数据。
jackduan
发表于 2025-2-11 16:41:16
通信简例
单片机像计算机发送0~255不断增大的数据。
使用串口1,定时器1作为波特率发生器,波特率9600,频率为22.1184MHZ。
T0作为定时器,延时。
#include"STC 15W4K.H"
void delay500ms(void)
{
unsigned char counter;
TMOD = 0x01;
TH0 = 0x70;
TL0 = 0x00;
TR0 = 1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0x70;
TL0=0x00;
counter++;
}
if(counter==50)
{
counter = 0;
}
}
}
void UART_init(void)
{
TMOD = 0x20; //定时器设置
TH1 = 0xFA;
TL1 = 0xFA;
TR1 = 1;
AUXR = 0x00;
SCON = 0x50;
}
viod UART_send_byte(unsigned char dat)
{
SBUF=dat;
while(!TI);
TI = 0;
}
void main()
{
unsigned char num = 0;
UART_init();
while(1)
{
UART_send_byte(num++);
delay500ms();
}
}
jackduan
发表于 2025-2-12 11:40:49
4.6 SSI数据通信
SSI采用6线制方式,两线电源,两路RS485,其中一路为时钟输出,另一路为数据输入,单片机向从机发送移位时钟的过程中完成从机返回数据的接收。
数据传输格式说明:当没有传送时,与单片机引脚连接的时钟线和数据线都是高电平状态在时钟信号的第一个下降沿,当前位置值被存储,在随后的时钟上升沿,数据从最高有效位(MBS)开始依次送出。一个完整的数据字传送完成后,数据线保持一段时间的低电平,直到准备好下一个值。
SSI通信中,一次传输的数据不是按字节计算,而是按位计算的,比如一次传输25位数据,要解决的问题就是如何将这25位数据接收进来并转换成一个长整数。
jackduan
发表于 2025-2-12 16:57:35
格雷码
格雷码不是权码,每一位没有确定的大小,不能直接进行比较大小和算术运算,因此单片机接收到的格雷码不能直接使用,需要经过一次码变换,变成自然二进制码。
格雷码所有相邻整数之间转换时,只有一位发生变化。最大数与最小数之间也仅有一个数不同,所以又叫格雷反射码或循环码。