Skip to content

jack-zheng/c51-start

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

c51-start

Notebook of learning c51 mcu

Env Setup

学习视频简介中作者给出了 baidu 网盘,里面又 Keil 和 STC 的软件,可以直接安装。其中涉及到关闭 Windows 防火墙的步骤,不关闭的话,License 注册软件不能打开,过程可以参考这篇(博客)[https://blog.csdn.net/Mirecz/article/details/108961731]

示例程序

P4 点亮 led

  1. 在目标位置新建文件夹, 命名位 '01 点亮 led'
  2. 打开 keil, 选择 Project -> New uVersion Project -> 选择新建的文件夹 -> 文件名设为 Project -> 确定
  3. 此时会弹出选择芯片的弹框, STC 系列不在列表中,选择同型号的 Atmel 89c52 代替即可。弹出 copy 启动文件选项,选否即可
  4. 展开目录树中 Target 1,在 Source Group 1 上右键选择 'Add new item to ...', 再选中 C File,输入文件名 main 创建主文件
  5. 在 main 中写入逻辑代码(右键可以直接插入头文件,很方便)
  6. 在 Build 按钮的同一行,有一个 Options for target 按钮(看上去像魔术棒) 点击,选择 Output 选项卡,勾选 'Create HEX File' 用来生成烧录文件
  7. 点击 Build 按钮,生成文件,看控制台输出,显示生成位置在目标文件夹的 Objects 下
  8. 打开 STC-ISP 软件,选择单片机型号 'STC89C52RC/LE52RC'
  9. 选择串口号,一般会自动适配
  10. 点击 打开程序文件,选择生成的 .hex 文件
  11. 点击 下载/编程 按钮,同时给开发板上电,软件右下侧会显示烧录状态
#include <REGX52.H>

void main() 
{
	P1=0x55; //1111 1110
	while(1)
	{
	}
}

P5 led 闪烁

过程和前面一样,这里额外介绍了一下 STC-ISP 软件的软件延时代码功能。

打开 STC,软件右侧有很多实用工具,其中一个是 软件延时计算器,根据开发板具体配置,选择 系统频率,指令集等参数,然后 CV 即可, 一秒延时示例代码如下

PS: 指令集那边选仔细了,不然结果会出问题,STC89C52RC 是在 STC-Y1 这个系列里的

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

需要注意的是,他在实现中调用了 _nop_() 方法,要使用这个还需要在引入另外一个头文件 #include <INTRINS.H>

P6 led 流水灯

这节主要的点是函数封装,没什么难度

#include <REGX52.H>
#include <INTRINS.H>

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void Delaynms(unsigned int nms)
{
	while(nms)
	{
		Delay1ms();
		nms--;
	}
}

void main()
{
	P1=0xFE; //1111 1110
	Delaynms(500);
	P1=0xFD; //1111 1101
	Delaynms(500);
	P1=0xFB; //1111 1011
	Delaynms(500);
	P1=0xF7; //1111 0111
	Delaynms(500);
	P1=0xEF; //1110 1111
	Delaynms(500);
	P1=0xDF; //1101 1111
	Delaynms(500);
	P1=0xBF; //1011 1111
	Delaynms(500);
	P1=0x7F; //0111 1111
	Delaynms(500);
}

P7 独立按键控制 led 亮灭

新增内容为通过读取按键状态控制灯状态

#include <REGX52.H>

/**
* left  P1.0
* up	P1.1
* down  P1.2
* right P1.3
*
* led 	P1.7
*/
void main()
{
	while(1)
	{
		if(P1_2==0 && P1_1==0)
		{
			// if down pushed, light the led
			P1_7 = 0;
		} else {
			// turn off led
			P1_7 = 1;
		}
	}
}

P8 独立按键控制 led 状态

增加了按键去抖效果

/**
* left  P1.0
* up	P1.1
* down  P1.2
* right P1.3
*
* led 	P1.7
* 轻触按键按下时会有 5-10ms 的抖动
*/
void main()
{
	while(1)
	{
		if(P1_2==0)
		{
			Delaynms(20);
			while(P1_2==0);
			Delaynms(20);
			P1_7=~P1_7;
		}
	}
}

P9 独立按键控制 led 显示二进制

声明一个 unsigned char LedNum=0 表示状态,按键按下时赋值给 P1 端口并取反达到效果,可惜我的开发板按键和 led 用的同一个 P 口,无法实现这个实验了

P12 动态数码管显示

  • 74HC245, 数据缓冲器,由于课程中的数码管时共阴极的,正极如果都接 IO 口,由于单片机 IO 口电流很弱,所以添加了这个 245 芯片,用来增强电流的。类似驱动电机的时候需要驱动芯片,原理时一样的
  • TM1640 两根线就能控制16位数码管,节省 IO
  • 74HC595 通过 3 根数据线 + 2 根电源线就可以控制 8 个数码管。串转并操作。三根数据线分别是电平线,时钟移位线和数据输出控制线。

代码实现中声明代码段的时候用了 code 关键字,这种设置可以将定义放到 ROM 中,避免占用 RAM 空间,每次使用的时候都去 ROM 中读取

根据开发板的教学视频,基于 74HC595 实现了动态扫描,发现硬件 debug 的能力还是不行,需要多实践

P13 模块化编程

主界面选择扳手突变,在 Editor 的 encoding 中选择 UTF-8 支持中文编码

include 后面引用 '<>' 和 "" 双引号的区别,前者是在安装目录中寻找文件,后者是在当前项目下寻找。其实可以混用,这个区别是查找的优先级

本节要点,掌握模块化代码。可以将函数抽出存放在 .c 文件中,并为其创建 .h 头文件。在主文件中通过引用 .h 文件的方式,达到重用代码的效果,很赞。

#ifndef __NIXIE_H__
#define __NIXIE_H__

void SegmentDisplay(unsigned char pos, unsigned char val);

#endif

P14 模块化编程

参考视频的代码,简单改一下端口映射就实现了代码迁移,调试很方便

P15 矩阵键盘

这节中的 IO 口弱上拉,强下拉的点讲的很好。正是因为这种特定,所以通过检测低电平状态(0)来判断按下,如果检测 1 会有强电流输入的危险

PS:使用 LCD1602 检测的时候,发现 ShowString 有 bug, 显示字符串的时候,第一位不显示。等写了 1602 之后修一下

还是要对比视频的代码才能把逻辑改对,不是很 6,就机会多练习。

P16 矩阵键盘密码锁

没啥新意,掌握了 P15 之后改改就行了

P18 按键控制流水灯模式和定时器时钟

中断控制开关:

  • EA:总开关
  • ET0: Timer0 开关
  • PT0:Timer0 优先级开关

寄存器 IE 和 XICON 控制系统中断源是否被允许。

IE 中断寄存器允许位寻址

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
IE A8H name EA - ET2 ES ET1 EX1 ET0 EX0

XICON 辅助中断控制寄存器, 允许位寻址

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
XICON C0H name PX3 EX3 IE3 IT3 PX2 EX2 IE2 IT2

上电复位后 IE/XICON 被清 0

中断规则:

  • 低优先级中断可被高优先级中断打断
  • 任何中断,一旦得到响应就不会被同等级中断打断

基础的优先级控制寄存器 IP,允许位寻址

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
IP B8h name - - PT2 PS(串口) PT1 PX1 PT0 PX0

89c51 在只有两种中断优先级,89c52 在他的基础上扩展了一位,有四种优先级。通过 IPH(不可位操作) 和 XIOCON 寄存器控制

上电复位后 IP/IPH 均为 00H

定时器/计数器 0/1 的控制寄存器 TCON,这个寄存器中还有集成有中断开关位。允许位寻址

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
TCON 88h name TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0

TF0: timer flag, 中断标志位,当计数器溢出时置为 1 当中断响应执行结束时,由硬件置为 0 (也可自己软件查询置为0)

TR0: timer run, 运行控制位, 置为 1 开始运行。

I开头的是外部中断设置,和 timer 没关系

定时器/计数器的工作模式寄存器 TMOD

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
TMOD 89h name GATE C/T M1 M0 GATE C/T M1 M0

P20 串口通信

管脚 P3.0/RxD, P3.1/TxD

主要寄存器

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
SCON 98h name SM0/FE SM1 SM2 REN TB8 RB8 TI RI
01010011
  • 可位寻址
  • SM0, SM1 设置位 0 1 表示工作在 8 位 UART 模式下
  • REN 允许接收数据
  • TI/RI: 发送/接收完成中断开关,共用中断通道。复位后为 0 ,接收/发送完成硬件置 1, 处理完逻辑之后。我们需要软件置0

模式控制位比较奇葩,是和电源管理寄存器共享位置的

SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
PCON 87h name SMOD SMOD0 - POF GF1 GF0 PD IDL
  • 不可位寻址
  • SMOD:为 1 时,时串行通信方式 1,2,3 的波特率加倍。
  • SMOD0 位 0 时,设置位串行通信模式
SFR name Address bit B7 B6 B5 B4 B3 B2 B1 B0
IE A8h name EA - ET2 ES ET1 EX1 ET0 EX0

如果要使用中断,只需控制 EA 和 ES 中断即可

demo 优先级需求小,寄存器可以不设置

P22 点阵屏显示图形

sfr: special function register, 特殊功能寄存器。e.g. sfr P0=0x80;

sbit: special bit, 特殊位声明。e.g. sbit P0_1=0x81; sbit p0_1=P0^1;

^=: 异或操作

单片机复位,所有 IO 口都是高电平

变量声明添加 code 关键字,将数据存到 ROM 中,节省 RAM 空间。缺点时这个变量不能在程序中更改。

我的开发板点阵布局和上面的不一样,不能实践。

P24 DS1302时钟

实验时先用 LCD 打印看看 8F 这个地址的保护位状态,开启之后,不能写入数据

芯片中时间以 BCD 码存储,如果要正确显示时间,调用 LCD 的 ShowHexNum 方法

2位BCD码计算方式

  • DEC=BCD/16*10 + BCD%16;
  • BCD=DEC/10*16 + DEC%16;

Keil 中定义常量的语法 #define DS1302_SEC 0x80 没有分号,没有等号

最后说的那个定时器检测按键消抖的功能感觉上应该挺实用

通过 MODE 设置显示模式的这种处理方式很新颖

还有一个 bug, 选中的位一直在闪烁,也就是 MODE 0 的时候还会进到 SetTime 的 loop 中,好奇怪,明天 debug 一下 问题解决了,switch-case 里面忘了加 break 了,导致显示模式下,设置模式也会被执行

P26 蜂鸣器

板子上时有源蜂鸣器,做不了,而且感觉这个也不酷,直接跳到 iic 章节

P28 AT24C02数据存储

随机读模式,在读之前会有一个虚拟写的过程,这个时用来改变内部指针的,不然光一个读命令只能读出当前计数器指向的地址数据,不具备随机指定地址的能力

很多国产 AT24C02 地址线内部默认都是接地的,外部改了也没用。这个bug害我查看了1小时,最后看10年前附送的代码才发现的,真是日了狗了!!

P28 AT24C02秒表

C语言中静态局部变量作用返回时什么?Java中没有这种概念。大概时方法内共享的吧,且一直存在

补完了之前定时器消抖的坑,挺有意思

P30 DS18B20温度显示

第一次写 init 函数的时候,从机响应那部分没做好,延时顺序搞错了。突然想到,我有没有可能通过单片级的计时器记录低电平时间,观察一下。等以后有了示波器之后,也可以检测一下个 init 过程

这个温度传感器时通过两个Byte存储信息的,中间涉及到一些类型转化取值的部分还是比较模糊,还有补码之类的内容。可能要等到看完视频学一下C语言之后再回头看看了

OneWire 通信对事件要求很严格,如果按键实用定时器中断,会打算总线通信,所有会闪屏,可以通过再总线操作函数中开关 EA 暂停计时器改善,不过这也导致定时器代码分散在各个模块中,是个小缺陷。IIC中线就没有这种问题。

P32 LCD1602功能函数代码

LCD_ShowString 的时候指针处理不是很懂,要看C的书

ShowSignedNum 里面 Number=-Number 的一些显示,也只听懂了一个大概

P34 PWM波

呼吸灯和PWM呼吸灯完成了,不过我没有直流电机,做不了最后的实验

P36 AD/DA

芯片不一样,都看完了再搜下资料实验一下

P38 红外遥控

基础实验完成了,电机联调缺少器件,就不做了。总的来说还是收获了不少。暂时先歇一歇,51算入门了,后面还想巩固的话要重新学一下C语言,然后找一些小项目做做,读读51的文档之类的。下一步打算一边做俄罗斯方块小项目,一边复习C语言。

回忆篇 - 一些常用元器件及其使用

  1. Hello world sample - turn on led
    • this setup cost me about 2 hours...
  2. Turn on/off relay
  3. Bee
  4. Button
    • when handle with button event, add delay to avoid shake.

数码管

  • 74HC595 用于扩展数码管
  • pin9 级联输出
  • A 数据输入位
  • pin11 数据记录位,给一个高电平脉冲作为标记(0-1-0)
  • pin12 数据输出位,一个高电平脉冲,引脚输出变为记录的样子

矩阵键盘

过程比较长,并没有想象的简单,但是可以通过慢慢的迭代来完成这个功能,比如先点亮数码管,到数码管显示两位,再到数码管根据参数显示。。。最后再到键盘显示

时钟芯片 DS1302

  • C51单片机的语句运行时间是 us 级别的
  • 通信规则:先发命令码,再读/写数据
  • 命令码可以告诉芯片你想做什么
  • 读出来的时间数值是十六进制的,比如读出来的秒位0x59 - 十进制显示为89, 需要在数码管显示的时候做下处理
  • 在写之前记得关闭写保护,你不能确定之前的程序有没有关闭,不然可能不能进行写的动作

AT24C02 断电保护

  • data change only happened when SCL is low
  • 数据读取的时候有四步,我一直以为只有三步的。。。
    • 输入操作 w
    • 输入地址
    • 输入操作 r
    • 读取返回值

PCF8591 实现AD/DA 转化

基本和上节内容一样,也是用了IIC协议,可以重用很多函数。 呼吸灯里面的实现算法还是很巧妙的。。。

串口通信

  1. 寄存器设置
  2. 波特率设置, 9600。 选定 11.0592M 的晶振,刚好整除
  3. 当数据接受完后, RI自动变1, 处理完后软件复位 0

很多概念还是很模糊,寄存器有哪些功能什么的,不过我还是打算先看下去,以后用需要的话再回头复习

LCD1602 显示

Busy Check 的时候很坑的一点, 当 E 从 0 变为 1 时, BF flag 才会填充到IO口。。。

定时,中断

  • STC-SPI 工具可以给出定时器的样码,简直骚操作 - 但是好像不怎么好使
  • 16位定时器需要手动重置 TH0, TL0 的初始值
  • MCU是单线程的。。。当进去EX0是其他中断是不运行的

红外信号软件解码 - VS1838B

  1. 引导码 + 用户码 + 数据码 + 数据反码 = 32 bit
  2. 起始码: 9ms 高电平 + 4ms 低电平
  3. 脉冲周期:1.125 - 0; 2.25 - 1
  4. 式例程序写的太垃圾了,把波段时常统计为放到了中间做重置,我楞是没读懂他的代码,做了几个中断实验才看出来,Fuck Damn It!!!
  5. 红外接收去收到的波形和遥控器发出来的刚好是反的,所以用下降沿触发的形式去检测刚好合适

LCD12864 display

  • 驱动形式和LCD1602很像,只实现了最基本的功能,进阶版的功能以后有用再说。显示使用的北部芯片是ST7920
  • 显示英文时,16x8, 中文 8x4
  • 行地址,奇怪的设定,好像是为了图片实现才搞得这一套地址系统, 文档上给的第三四行地址还不对?擦擦擦 + 1st 80H-8FH + 2nd 90H-9FH + 3rd 88H-AFH + 4th 98H-BFH
  • 很棒的位图制作教程,不需要任何额外的工具 How To,不过教程上有一点写错了,需要选择 Horizontal, 选 Vertical 会乱码的!!!软件名字 'LCDAssistant'

DS18B20

单总线通信,简直牛皮。51一个语句执行时间是多少?很神奇。

相关资料

74595 芯片阅读:

functional diagrame

通过上面的功能图可以比较直观的理解74595的工作原理:

  • 使用时通过 DS 引脚串行输入数据
  • SHCP - shift register clock pin, 相当于一个开关,出发时数据进入
  • STCP 相当于一个闸,开启后数据统一输出到 Qn pin 脚
  • Q7S,相当于芯片扩容,一片只能存8位,两片的话可以扩展到16位

引脚说明图:

pin desc

创建自己的库文件

试着用网上的教程走了一遍,都没能成功,菜菜菜。。。有机会再试试把,浪费一晚上时间了

采坑实录

  1. 习惯了 Java 语法,习惯性的没有把变量声明放到函数体第一排,排了半天的错,擦擦擦~

TODO

  1. 搜索一下怎么给C程序定制注释,让他看上去更专业

About

Notebook of learning c51 mcu

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published