一,单片机
单片机学习总结出了一个重要经验:程序的编写以实现需以A2开发板原理图和相关元器件的手册为基础来编写
- 矩阵按键
电路图
工作原理
若我们想选中第一个S1,则先令P1为0xFF(1111 1111),使所有都设置为高电平,然后使P17为0,即可选中1,若我们想让S1输出为1,则创建一个unsigned char Key函数,返回值为1即可,其他按键同理
#include <STC89C5xRC.H>
#include "Delay.h"
unsigned char MatrixKey()
{
unsigned char KeyNumber=0;
P1=0xFF;
P13=0;
if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=1;}
if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=5;}
if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=9;}
if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=13;}
P1=0xFF;
P12=0;
if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=2;}
if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=6;}
if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=10;}
if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=14;}
P1=0xFF;
P11=0;
if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=3;}
if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=7;}
if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=11;}
if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=15;}
P1=0xFF;
P10=0;
if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=4;}
if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=8;}
if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=12;}
if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=16;}
return KeyNumber;
}
注意按键消抖
2. 定时器
介绍:定时器是单片机的内部资源,可提高cpu运行效率,也可在一定条件下替换Delay函数
51单片机有多个定时器,其中T0与T1(串口通信)常用
工作原理
单片机内部有一个类似闹钟的装置,每隔一段时间计数器加一,当达到一定值时则触发中断系统,中断当前的程序而执行定时器程序,例如一个学生在写作业被父母叫去吃饭。
我们可以按照此图来写定时器程序,以T0定时器为例,图二是控制定时器模式,高四位为T1定时器,低四位为T0定时器,我要启用T0定时器则给前面高四位置1,设置第四位即可,GATE不用设置置0即可,CT为0时为定时器模式,所以CT为0,M1和M2是设置定时器模式,定时器有多个模式,如十六位自动重载,十六位,八位自动重载等,而T0定时器十六位常用,M1 M0 = 01:方式1,是16位定时器/计数器。M1 M0 = 10:方式2,8位自动重装定时器/计数器。所以给M1为0,M2为1,CT为1时为计时器功能,CT为0时为定时器功能,所以应该给CT置0,根据图中TR0置1,TL0与TH0分别是设置低位设置定时初值和高位设置定时初值,如我要定时一毫秒使得计数器加一则令TL0 = 0x18,TH0 = 0xFC,也可以通过stc-isp里的定时计算器设置自己想要的值,如5ms加1,当定时器达到一定的值使TF0为1,执行中断程序,当中断程序完成时,记住使TF0置0,也意味着定时器初始化时也得置0,ET0,EA,PT0是设置是否要执行中断程序和中断优先级因此得出以下程序
#include <STC89C5xRC.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 0x18;
TH0 = 0xFC; //1毫秒定时
TF0 = 0;
TR0 = 1;
ET0=1;
EA=1;
PT0=0;
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
- 串口通信
串口有多个模式,如下图所示
工作原理
与前面定时器想类似,不过使用的是定时器T1,但一个个配置过于繁杂,我们可以借助stc-isp波特率计算器和串口助手配置相关模式,定时器配置也建议使用stc-isp以提高代码编写效率
结合stc-isp波特率计算器得出代码
#include <STC89C5xRC.H>
void UartInit(void) //4800bps@11.0592MHz
{
PCON &= 0x80; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFA; //设置定时初始值
TH1 = 0xFA; //设置定时重载值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
}
void UART_SendByte(unsigned char Byte)
{
SBUF=Byte; // 根据硬件原理,操作寄存器
while(TI==0); // 操作寄存器,检测是否完成
TI=0; // 按要求重新赋值为0
}
- LED点阵屏
工作原理
51单片机是88的点阵屏,相当于有88个小灯,根据图一可知,P0口是控制列,而D0-D7控制行,可与前面的矩阵按键相类比,有许多的相似性,但不同的是控制行的D0-D7口并非由P口控制,需借助74HC595,74HC595工作原理图参考图二,首先先传入一位到SER(P34),然后通过上升沿移位SERCLK(P36)送到上升沿锁存RCLK(P35)**(注意,送二进制位时需使SERCLK置1,然后再置0)**后推送到QA-D0,当D0为0时,第一行会亮,又因为其过程只能传入一位,所以得需要运用for循环把其余四位传入到D1-D7,代码如下
#include <STC89C5xRC.H>
#include "Delay.H"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i);
SCK=1;
SCK=0;
}
RCK=1;
RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column, Data)
{
_74HC595_WriteByte(Data);
MATRIX_LED_PORT=~(0x80>>Column);
Delay(1);
MATRIX_LED_PORT=0xFF;
}
void main()
{
SCK=0;
RCK=0;
while(1)
{
MatrixLED_ShowColumn(0,0x80);
MatrixLED_ShowColumn(1,0x40);
MatrixLED_ShowColumn(2,0x20);
MatrixLED_ShowColumn(3,0x10);
}
}
- DS1302实时时钟
介绍:DS1302是一种低功耗的实时时钟芯片
工作原理
DS1302具体如何运行请主要参考图二和图三,根据图三可知有字节写入和字节读取两个重要功能,而写入到哪里或者读取哪里则需参考图二,图二包含着秒,时和年等相关地址,读的地址是写的地址加一,代码的编写最重要的是参考图三,可知想要写入或读取时间都得使CE置1(注意使用前初始化,即使CE置0,SCLK置0),然后从低位开始输入,和LED点阵屏一样,都是一位一位的传入,而依靠SCLK(P36),因此得使用for循环把八位全部写入或读取,写入完成后再使得CE置0即可,而秒的变化,时钟的变化不需编写,芯片会自己编写,而我们只需要靠写入数据来设置时钟开始时间,然后在读取数据得到实时时钟,注意,写入数据一般我们都是写入十进制,但该芯片写入得用BCD码,因此写入数据时应将十进制转化为BCD码,读数据时将BCD码转化为十进制即可,代码如下
#include <STC89C5xRC.H>
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
unsigned char DS1302_TIME[]={24,4,16,12,59,55,6};
void DS1302_Init(void)
{
DS1302_CE=0;
DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command, Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0; //完成一次操作,释放IO
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01; //
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0; //根据时序操作
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1; //重复置1是去掉一个周期,为的是满足时序
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0; // 如果不加这一行,将显示全0
return Data;
}
void DS1302_SetTime(void)
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_TIME[0]/10*16+DS1302_TIME[0]%10);
DS1302_WriteByte(DS1302_MONTH,DS1302_TIME[1]/10*16+DS1302_TIME[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_TIME[2]/10*16+DS1302_TIME[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_TIME[3]/10*16+DS1302_TIME[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_TIME[4]/10*16+DS1302_TIME[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_TIME[5]/10*16+DS1302_TIME[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_TIME[6]/10*16+DS1302_TIME[6]%10);
DS1302_WriteByte(DS1302_WP,0x00);
}
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_TIME[0]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_TIME[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_TIME[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_TIME[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_TIME[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_TIME[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_TIME[6]=Temp/16*10+Temp%16;
}
- 蜂鸣器
介绍
工作原理
51单片机是无源蜂鸣器,由P25控制蜂鸣器是否发出声音
二,数据结构(C)
单链表与双链表
学习经验:数据结构的学习建议结合win系统自带的画图工具,在画图工具中用框把链表节点表现出来,让人更容易理解其中的原理
何为链表,以画图工具来演示会方便理解,链表即每个节点都一一相连,可从第一个节点访问到最后一个节点,和数组很相似,但数组与链表在内存的储存有着很大的区别
数组:
连续存储:数组中的元素在内存中是连续存储的,也就是说,数组的所有元素在内存中是相邻的。
快速访问:由于数组的元素是连续存储的,因此可以通过索引快速访问任何一个元素,时间复杂度为 O(1)。
固定大小:数组的大小是固定的,在创建时需要指定大小,无法动态改变。
链表:
非连续存储:链表中的元素在内存中不是连续存储的,而是通过指针相互连接起来的。
动态大小:链表的大小可以动态增长或减小,因为每个节点只存储了指向下一个节点的指针,所以可以在运行时动态地分配内存。
遍历复杂度:在链表中,访问特定位置的元素需要从头开始遍历直到找到目标位置,所以访问的时间复杂度是 O(n),其中 n 是链表的长度。
插入和删除效率高:由于链表的节点可以动态分配和释放,插入和删除元素的效率较高,平均时间复杂度为 O(1)。
头插法与尾插法(以画图工具演示)
头插法
尾插法
- 单链表与单循环链表
(1)介绍
单链表:
单链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。
每个节点只有一个指针,指向下一个节点,最后一个节点的指针指向 NULL(空)。
单循环链表:
单循环链表是单链表的一种变体,其特点是最后一个节点的指针不指向 NULL,而是指向第一个节点,形成一个环形结构。
单循环链表可以通过任何一个节点开始遍历整个链表,因为整个链表形成了一个循环。
单循环链表可以用于模拟循环操作,例如循环队列等数据结构。
(2)画图工具演示
(3)相关代码实现(加减与遍历链表,加包括头插法与尾插法)
单链表
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct Node {
int data;
struct Node* next;
}Node;
Node* initList() {
Node* L = (Node*)malloc(sizeof(Node));
L -> data = 0;
L -> next = NULL;
return L;
}
void headInsert(Node* L, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node -> data = data;
node -> next = L->next;
L -> next = node;
L -> data ++;
}
void tailInsert(Node* L, int data) {
Node* node = L;
for(int i = 0; i < L -> data; i++) {
node = node->next;
}
Node* n = (Node*)malloc(sizeof(Node));
n -> data = data;
n -> next = NULL;
node -> next = n;
L -> data ++;
}
int delete(Node* L, int data) {
Node* preNode = L;
Node* node = L -> next;
while(node){
if(node -> data == data) {
preNode -> next = node->next;
free(node);
L -> data --;
return TRUE;
}
preNode = node;
node = node -> next;
}
return FALSE;
}
void printList(Node* L) {
Node* node = L -> next;
while(node){
printf("node = %d\n", node -> data);
node = node -> next;
}
}
int main() {
Node* L = initList();
headInsert(L, 1);
headInsert(L, 2);
headInsert(L, 3);
headInsert(L, 4);
headInsert(L, 5);
tailInsert(L, 6);
tailInsert(L, 7);
printList(L);
if (delete(L, 3)) {
printf("success delete\n");
}
else {
printf("fail delete\n");
}
printList(L);
return 0;
}
单循环链表
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct Node {
int data;
struct Node* next;
}Node;
Node* initList() {
Node* L = (Node*)malloc(sizeof(Node));
L -> data = 0;
L -> next = L;
return L;
}
void headInsert(Node* L, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node -> data = data;
node -> next = L -> next;
L -> next = node;
L -> data ++;
}
void tailInsert(Node* L, int data) {
Node* n = L;
Node* node = (Node*)malloc(sizeof(Node));
node -> data = data;
while(n -> next != L){
n = n -> next;
}
node -> next = L;
n -> next = node;
L -> data ++;
}
int delete(Node* L, int data) {
Node* preNode = L;
Node* node = L -> next;
while(node != L){
if(node -> data == data){
preNode -> next = node -> next;
free(node);
L -> data --;
return TRUE;
}
preNode = node;
node = node -> next;
}
return FALSE;
}
void printList(Node* L) {
Node* node = L -> next;
while(node != L){
printf("%d->", node -> data);
node = node -> next;
}
printf("NULL\n");
}
int main()
{
Node* L = initList();
headInsert(L, 1);
headInsert(L, 2);
headInsert(L, 3);
headInsert(L, 4);
headInsert(L, 5);
tailInsert(L, 6);
tailInsert(L, 7);
printList(L);
delete(L, 4);
delete(L, 7);
printList(L);
return 0;
}
- 双链表与双循环链表
(1)介绍
双链表:
双链表中的每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。
双链表可以实现双向遍历,可以从前向后或从后向前遍历链表。
双链表的插入和删除操作相对于单链表来说更灵活,因为可以直接通过前向指针或后向指针找到相邻节点。
双链表的缺点是每个节点需要额外的一个指针,占用的内存空间相对更多。
双循环链表:
双循环链表是双链表的一种变体,其特点是第一个节点的前向指针指向最后一个节点,最后一个节点的后向指针指向第一个节点,形成一个循环结构。
双循环链表可以通过任何一个节点开始遍历整个链表,因为整个链表形成了一个循环。
双循环链表适用于需要循环操作的场景,例如循环队列等。
(2)画图工具演示
双链表
双循环链表
(3)代码实现
双链表
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct Node {
int data;
struct Node* pre;
struct Node* next;
}Node;
Node* initList() {
Node* L = (Node*)malloc(sizeof(Node));
L -> data = 0;
L -> pre = NULL;
L -> next = NULL;
return L;
}
void headInsert(Node* L, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node -> data = data;
node -> next = L -> next;
node -> pre = L;
if (L -> next) {
L -> next -> pre = node;
L -> next = node;
}
else {
L -> next = node;
}
}
void tailInsert(Node* L, int data) {
Node* node = L;
Node* n = (Node*)malloc(sizeof(Node));
n -> data = data;
while (node -> next) {
node = node -> next;
}
n -> next = node -> next;
node -> next = n;
n -> pre = node;
L -> data ++;
}
int delete(Node* L, int data) {
Node* node = L->next;
while (node) {
if (node -> data == data) {
node -> pre -> next = node -> next;
if (node -> next) {
node -> next -> pre = node -> pre;
}
L -> data --;
free(node);
return TRUE;
}
node = node -> next;
}
return FALSE;
}
void printList(Node* L) {
Node* node = L -> next;
while (node) {
printf("%d -> ", node -> data);
node = node -> next;
}
printf("NULL\n");
}
int main()
{
Node* L = initList();
headInsert(L, 1);
headInsert(L, 2);
headInsert(L, 3);
headInsert(L, 4);
tailInsert(L, 5);
tailInsert(L, 6);
printList(L);
delete(L, 6);
printList(L);
return 0;
}
双循环链表
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* pre;
struct Node* next;
}Node;
Node* initList() {
Node* L = (Node*)malloc(sizeof(Node));
L -> data = 0;
L -> pre = L;
L -> next = L;
return L;
}
void headInsert(Node* L, int data) {
Node* node = (Node*)malloc(sizeof(Node));
node -> data = data;
node -> next = L -> next;
node -> pre = L;
L -> next -> pre = node;
L -> next = node;
L -> data ++;
}
void tailInsert(Node* L, int data) {
Node* node = L;
while (node->next != L) {
node = node -> next;
}
Node* n = (Node*)malloc(sizeof(Node));
n -> data = data;
n -> pre = node;
n -> next = L;
L -> pre = n;
node -> next = n;
L -> data ++;
}
int delete(Node* L, int data) {
Node* node = L -> next;
while (node != L) {
if (node -> data == data) {
node -> pre -> next = node -> next;
node -> next -> pre = node -> pre;
free(node);
L -> data --;
return 1;
}
node = node -> next;
}
return 0;
}
void printList(Node* L){
Node* node = L->next;
while(node != L){
printf("%d -> ", node->data);
node = node->next;
}
printf("NULL\n");
}
int main()
{
Node* L = initList();
headInsert(L, 1);
headInsert(L, 2);
headInsert(L, 4);
headInsert(L, 5);
printList(L);
tailInsert(L, 6);
tailInsert(L, 7);
printList(L);
delete(L, 7);
printList(L);
return 0;
}
三, C++
目前学到类和对象中的友元,以黑马教学视频为主,因在考虑单片机自己想实现的程序的实现与这几天课较多,暂不过多赘述,后面有时间补完
下周计划
- 51单片机基本要学完
- 学完c++类和对象
- 试试力扣算法题库并总结相关题目和学习经验
- 继续数据结构学习