硬件------51单片机

发布于:2025-06-19 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

一.基本概念

        1.裸机程序  BSP 

        2.linux驱动部分

       3.CPU、MPU、GPU、MCU

        4.寄存器

        5.RAM和ROM

二.环境搭建

1.keil安装

2.STC-ISP

三.工程  

        1.LED

                1.原理图

                2.位运算

                3.代码

        2.digister

                1.原理图

                2.代码

        3.key

                1.  原理图

                2.代码

        4.中断

        1.概念

        2.五个中断源

        3.中断流程

        4.中断向量

        5.中断和轮询区别

        6.代码

                1.外部中断​编辑

                2.定时器中断

       5.定时器

        1.定时器计算

        1.概念

        2. 200Hz,50%占空比的PWM怎么计算

        2.代码

6.蜂鸣器

        1.原理图

        2.代码

        3.占空比

  7.uart

        1.串口通信

        2.寄存器

        3.代码

  8.ds18b20

                1.时序图

                        1.复位存在

                        2. 写0,1

                        3.读0,1

                2.代码


一.基本概念

        1.裸机程序  BSP 

                BSP:bord suppord pack  板级支持包

                就是程序编写的内容是没有操作系统的,直接通过代码去控制寄存器,让硬件按照要求去工作。

                主要内容:51单片机     IMAX6ULL

        2.linux驱动部分

                在裸机BSP程序的基础上,把对硬件管理的方法,算法,流程,挪到linux系统中,让linux能识别,操作硬件,相当于程序建立在linux系统上。

                主要内容:系统移植   驱动

       3.CPU、MPU、GPU、MCU


1. CPU(Central Processing Unit)中央处理器

  • 功能: 电脑和大多数电子设备的“核心大脑”,负责执行程序指令、进行运算和控制数据流。

  • 常见于: 电脑、手机、平板等。

  • 特点: 通用性强,适合复杂多任务处理。


2. GPU(Graphics Processing Unit)图形处理器

  • 功能: 专门用于图形、图像、视频的渲染和并行处理,也被广泛用于AI计算。

  • 常见于: 游戏显卡、AI训练服务器、手机、图形工作站。

  • 特点: 并行处理能力强,适合执行大量相同操作(如矩阵计算)。


3. MPU(Microprocessor Unit)微处理器单元

  • 功能: 一种更小、更简单的 CPU,常用于嵌入式系统中(如洗衣机、微波炉)。

  • 常见于: 工业控制、小型智能设备。

  • 特点:

    • 没有内置内存(RAM、ROM)或IO接口,需要外接。

    • 更灵活但需要更多外围电路。


4. MCU(Microcontroller Unit)微控制器单元

  • 功能: 一体化的嵌入式系统,集成了 CPU(或MPU)、内存、I/O接口等。

  • 常见于: 家电、汽车、智能硬件、IoT设备。

  • 特点:

    • 集成度高(“一颗芯片搞定一切”)

    • 功耗低、成本低、适合简单控制任务


简单对比总结表:

缩写

全称

用途

是否集成RAM/ROM

应用举例

CPU

Central Processing Unit

通用计算

PC、笔记本、服务器

GPU

Graphics Processing Unit

图像处理、并行计算

显卡、AI服务器

MPU

Microprocessor Unit

嵌入式计算(需要外设)

工控板、定制电子系统

MCU

Microcontroller Unit

嵌入式控制(单芯片系统)

空调、遥控器、智能灯泡


如果你想了解这些在某个具体应用(如AI、嵌入式、机器人)中的角色,我可以进一步解释。

        4.寄存器

        在数字电路中,用来存放二进制数据或代码的电路称为寄存器。

        外设寄存器有固定的地址。

        5.RAM和ROM

        

ROM(只读存储器)和 RAM(随机存取存储器)是计算机中两种常见的内存类型,它们在功能、特性和用途方面有显著区别。以下是详细的概念与对比:


一、ROM(Read-Only Memory)只读存储器

1. 概念:

  • ROM 是只能读取、不能随意写入的内存。

  • 数据在断电后仍然保留,属于非易失性存储器

  • 通常由厂家在生产时写入数据,主要用于保存固件(Firmware),如 BIOS(基本输入输出系统)。

2. 特点:

  • 数据不可更改或只能有限更改(如 EEPROM 可多次擦写,但速度慢)。

  • 通常存储启动程序、硬件初始化信息等关键内容。

  • 启动时首先读取 ROM 中的内容来引导系统。


二、RAM(Random Access Memory)随机存取存储器

1. 概念:

  • RAM 是可读写的内存,数据可以随时读写。

  • 数据在断电后会丢失,属于易失性存储器

  • 是 CPU 执行程序时的临时工作区域,用来存储正在运行的程序和数据。

2. 特点:

  • 访问速度快,是系统运行的关键。

  • 包括两类:

    • DRAM(动态 RAM):常用作主内存,成本低,需不断刷新。

    • SRAM(静态 RAM):速度更快,用于缓存,如 CPU 的缓存(Cache)。


三、ROM 与 RAM 的主要区别对比表

项目

ROM

RAM

全称

Read-Only Memory

Random Access Memory

读写性

通常只读

可读可写

易失性

非易失性(断电不丢失)

易失性(断电数据丢失)

用途

存储固件、启动程序(如 BIOS)

存储临时数据和程序运行信息

是否可修改

通常不可修改(或需特定方法修改)

可随时读写

速度

较慢

较快

例子

BIOS、固件、游戏卡带中的程序

操作系统加载后使用的内存、缓存等


四、简单类比

你可以把它们比作:

  • ROM:像一本印刷好的说明书,只能看不能写(或要特别工具才能改),断电不会丢失。

  • RAM:像一块写字板,你可以随便写写擦擦,但一断电内容就没了。


如需我进一步解释 EEPROM、Flash ROM 或是 RAM 的扩展类型如 DDR4/DDR5 等,也可以继续问我!

二.环境搭建

1.keil安装

2.STC-ISP

        下载使用。

三.工程  

        1.LED

                1.原理图

从原理图得知是P2寄存器,控制8个led灯,高电平关,低电平开。

                2.位运算

    &= :是让指定位置0;

           P2 &= ~(1 << 0):就是让bit0位为0,打开。
    |=:是让指定位置1;

           P2 |= 1 << 0 :就是让bit0位为1,关闭。
    ^=:是让指定位反转;

           P2 ^= 1<< 0:就是让bit0位反转

                3.代码

led.c

#include "led.h"
#include <reg52.h>

//初始化
void init_led(void)
{
	P2 = 0xff;
}
//打开某个灯
void led_on(unsigned char n)  //1111 0000
{
	
	P2 = ~n;
}
//打开所有灯
void led_all_on()
{
	P2 = 0x00;
}
//关闭某个灯
void led_off(unsigned char n)
{
	P2 = n;
}
//关闭所有灯
void led_all_off()
{
	P2 = 0xff;
}
//反转某个灯
void led_nor(unsigned char n)
{
	P2 ^= n;
}
//反转所有灯
void led_all_nor()
{
	P2 ^= 0xff;
}

main.c

#include <reg52.h>   //register
#include "led.h"
//延时
void Delay(unsigned int xms)
{
	while (xms--) //每次需要10us
	{

	}
}
int main(void)
{
	unsigned char i = 1;
	int n = 0;
	init_led();
	//全部反转
//	while(1)
//	{
//	 	led_all_nor();
//		Delay(0x3fff);
//	}
	//来回跑流水灯
	while(1)
	{
		for(n = 0;n < 7;++n)
		{
			led_on(i);
			Delay(0x3fff);
			i <<= 1;
			if(n == 6)
			{
				for(n = 0;n < 7;++n)
				{
					led_on(i);
					Delay(0x3fff);
					i >>= 1;		
				}
			}
		}
		
	}
}

led.h

#ifndef _LED_H_
#define _LED_H_

extern void init_led(void);
extern void led_on(unsigned char n);
extern void led_all_on();
extern void led_off(unsigned char n);
extern void led_all_off();
extern void led_nor(unsigned char n);
extern void led_all_nor();

#endif

        2.digister

                1.原理图

        有四个显示,分别由P10,P11,P12,P13控制,当为高电平的时候,打开相应的数码管。

        abcdefg dp由P0寄存器控制,当为1的时候,相应的位置亮起来。

                2.代码

 digister.c

#include <reg52.h>
#include "digiter.h"

void bit_select(int n)
{
	P1 &= ~(0x00ff << 0); //全部置0
	P1 |= (1 << n);       //bit n位置1
}

void seg_select(int n)
{	
	//定义一个显示0~9数字对应寄存器状态的数组
	code unsigned char segs[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
	P0 = segs[n];
}

void display(int n)
{
	int t = 0;
	while(n != 0)
	{
		int m = n % 10;
		P0 = 0;
		bit_select(t++);
		seg_select(m);
		Delay(300);
		n /= 10;
	}

}

        code是把这个数组变量放在ROM区,不是放在RAM区。

main.c

#include <reg52.h>
#include "digiter.h"
void Delay(unsigned int num)
{
	while(num--)
	{
	
	}
}

int main(void)
{

	int i = 0;
	while(1)
	{
		//for(i = 0;i < 10;++i)
		//{
		//	bit_select(0);
		//	seg_select(i);
		//	Delay(0xffff);
		//}
        display(i++);
		if(i > 9999)
		{
			i = 0;
		}
	}

}

digister.h

#ifndef _DIGITER_H_
#define _DIGITER_H_

extern void bit_select(int n);
extern void seg_select(int n);


#endif

        3.key

        1.  原理图

这是输入,当按键按下去之后,拉低P1,从而通过判断P1的状态而做相应的处理。 

        2.代码
void init_key(void)
{
	P1 |= (0x0f << 4);
}

int key_pressed(void)
{
	int ret = 0;
	if((P1 & (1 << 4)) == 0)
	{
		ret = 1;
	}
	else if((P1 & (1 << 5)) == 0)
	{
		ret = 2;
	}
	else if((P1 & (1 << 6)) == 0)
	{
		ret = 3;
	}
	else if((P1 & (1 << 7)) == 0)
	{
		ret = 4;
	}

 	return ret;
}

        4.中断

        1.概念

        当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。

        2.五个中断源

        51单片机有五个中断源:外部中断0(INTO)定时器0中断、外部中断1(INT1)、定时器1中断、串口(UART)中断、定时器2中断。对应的中断号。

        3.中断流程

1.中断源发出中断请求
2.CPU检查是否响应中断及该中断源是否被屏蔽;
3.检测中断优先级;
4.保护现场;
5.执行中断服务函数;
6.恢复现场

        4.中断向量

        中断向量表:是一个函数指针数组,数组里面保存中断服务函数的入口地址。51单片机的中断向量有五种:外部中断0/1,timer0/1,串口。

        5.中断和轮询区别

中断(Interrupt)和轮询(Polling)是两种常见的CPU与外设通信方式,它们在处理外设请求、数据传输等方面有明显区别:


一、定义

方式

定义

轮询(Polling)

CPU定期主动查询外设是否需要服务,一直轮询外设状态。

中断(Interrupt)

外设在需要服务时主动向CPU发出中断请求,CPU暂停当前工作来响应外设。


二、工作原理

🔁 轮询

  • CPU执行主程序的同时,定期检查设备状态。

  • 如果设备准备好了,就处理它的请求;否则继续主程序。

  • 不停检查,浪费资源。

中断

  • 外设完成某个操作或需要处理时,向CPU发送中断信号。

  • CPU暂停当前任务,保存现场,跳转到中断服务程序(ISR)处理外设请求,处理完再恢复原任务。


三、对比总结

项目

轮询(Polling)

中断(Interrupt)

主动方

CPU

外设

CPU效率

低(忙等待)

高(只在需要时才响应)

实时性

差,响应不及时

好,可快速响应事件

实现复杂度

简单

复杂(需中断机制、ISR等)

适用场景

少量设备、实时性要求不高的场景

设备多、实时性要求高的场景

资源占用

高,占用CPU时间

低,CPU可做其他任务


举个例子

假设你在等一个包裹:

  • 轮询:你每5分钟就下楼查看快递到了没(浪费时间、效率低)。

  • 中断:快递员来了按门铃,你再下楼取(只在必要时行动,效率高)。


如果你需要进一步了解硬件电路实现或在单片机(如STM32、8051)中如何设置中断和轮询,我也可以继续说明。

        6.代码

        1.外部中断

int g_i;
void eint0_handler(void) interrupt 0
{
	++g_i;
}
void eint1_handler(void) interrupt 2
{
	--g_i;
}

void init_einto(void)
{
	P3 |= (1 << 2) | (1 << 3);
	IE |= (1 << 7) | (1 << 0) |( 1<< 2);
	TCON |=  (1 << 0) | (1 << 2);

}
                2.定时器中断

 

#include <reg52.h>
#include "timer.h"
#include "led.h"

#define HZ200 63231

unsigned g_n = 0;
void timer_handler(void) interrupt 1
{
//	TH0 = 64613 >> 8;
//	TL0 = 64613;
//	++g_n;
//	if(g_n >= 100)
//	{
//		led_all_nor();
//		g_n = 0;
//	}
	TH0 = HZ200 >> 8;
	TL0 = HZ200;
	P2 ^= (1 << 1);

}

void init_timer(void)
{
	TMOD &= ~(0x0f << 0);
	TMOD |= (1 << 0);
//	TH0 = 64613 >> 8;
//	TL0 = 64613;
	TH0 = HZ200 >> 8;
	TL0 = HZ200;

	IE |= (1 << 7) | (1 << 1);
	TCON |= (1 << 4);

}
	TH0 = HZ200 >> 8;
	TL0 = HZ200;
	P2 ^= (1 << 1);

         这里高将counter的值的高八位保存在TH0中,所以将counter向右移动8位,那么低八位原来的位置就变成高八位,一个TH0占8位,刚好存放高八位。

        低八位直接赋值,高八位丢失,那么TL0刚好存放低八位。

       5.定时器

        工作原理:配置TMOD确认工作模式和计数来源,再向TH0/TL0写入计数初值,工作开始后计数溢出时,触发中断服务函数。

        1.定时器计算

        以16位的定时器举例,如何计算定时器

        16位 = 2^16 - 1 = 65535,定义计数的值位unsigned short 类型,当超过65535的时候,counter0就会变成0。

        晶振的频率为12MHz,12分频之后,cpu 的工作频率就是1MHz。

        假如要一个1ms刷新的值,那么 1MHz的情况下, 1 / 1M秒,counter0加1,也就是1us加1,要想1ms刷新,那么就需要conunter就要加1000次,也就是将counter的初始值设定为65535-1000 = 64534,这样1000次之后coeunter变成0。实现1ms。

        65535 - 0.001 /(1 / 1000000) =  65535 - 1000 = 64535.

        2.PWM

        1.概念

PWM(脉宽调制,Pulse Width Modulation)是一种通过改变**脉冲信号高电平持续时间(即“脉宽”)**来控制模拟量输出的技术。它常用于电机控制、LED调光、音频合成等应用中。


✅ 一、PWM是什么?

PWM 是一种数字方式模拟连续模拟信号的方法:

  • 高电平表示“开”,低电平表示“关”

  • 通过控制高电平持续的比例(占空比)来调节输出的平均电压

  • 虽然是开关信号,但其平均电压对外表现为模拟值


✅ 二、PWM的波形特征

PWM波形是一种周期性方波,每一个周期内:

  • 有一段时间是高电平(ON)

  • 剩余时间是低电平(OFF)


✅ 三、PWM的关键指标

指标名称 含义说明
频率(Frequency) PWM信号的周期性重复速率,单位是 Hz。频率 = 1 / 周期时间。
周期(Period) 一次完整高+低电平持续的总时间。周期 = 高电平时间 + 低电平时间。
占空比(Duty Cycle) 高电平持续时间占一个周期的百分比。占空比 = 高电平时间 / 总周期时间 × 100%
分辨率(Resolution) PWM占空比的可调精度,常用位数表示,如8位(256级)、10位(1024级)等。
幅度(Amplitude) 高电平电压的大小,例如3.3V、5V等。通常由电源电压决定。

✅ 四、PWM平均电压计算

PWM 实际输出的 平均电压 可以表示为:

Vavg=占空比×V高电平V_{\text{avg}} = 占空比 \times V_{\text{高电平}}

例如:5V PWM,占空比为 60% ⇒ 平均电压 = 5V × 0.6 = 3V


✅ 五、PWM应用举例

应用场景 PWM作用
LED调光 改变LED亮度(占空比高 → 更亮)
电机调速 控制电机平均电压,调节转速
音频输出 生成模拟音频信号
伺服控制 控制舵机角度(特定脉宽映射角度)

如果你在使用具体平台(如STM32、Arduino、ESP32)开发PWM应用,我也可以帮你讲解代码和硬件细节。是否需要这方面的帮助?

        2. 200Hz,50%占空比的PWM怎么计算

        

        200hz 就是 1 / 200 = 0.005秒刷新一次,PWM占空比是50%,也就是高低电平的时间各占一半,那么高电平的保持时间为0.0025,

        65535 - 0.0025 / (1 / 1000000) =  65535  -  2500 = 63235.

        2.代码

编写程序实现按下不同的按键使蜂鸣器产生不同的音调

6.蜂鸣器

        1.原理图

        

        2.代码

temer.c

#include <reg52.h>
#include "timer.h"
#include "key.h"

unsigned int hz = 100;

void timer_handler(void) interrupt 1
{
	TH0 = 65535 - (11059200 / (24 * hz)) >> 8;
	TL0 = 65535 - (11059200 / (24 * hz));
	P2 ^= (1 << 1);
}

void init_timer(void)
{
	TMOD &= ~(0x0f << 0);
	TMOD |= (1 << 0);
	TH0 = 65535 - (11059200 / (24 * hz)) >> 8;
	TL0 = 65535 - (11059200 / (24 * hz));

	IE |= (1 << 7) | (1 << 1);
	TCON |= (1 << 4);

}

main.c

#include <reg52.h>
#include "timer.h"
#include "key.h"
#include "digiter.h"
#include "delay.h"
int main(void)
{
	init_timer();
	init_key();
	while(1)
	{
		int key = key_pressed();
		if(key == 1)
		{
			hz = 100;
		}
		else if(key == 2)
		{
			hz = 200;
		}
		else if(key == 3)
		{
			hz = 400;
		}
		else if(key == 4)
		{
			hz = 600;
		}
		display(hz);
	
	}

}

        3.占空比

        通过调节一次中断后的计数器,也就是高电平变成低电平产生中断之后,计数器时间变化,同理,低电平变成高电平之后也进行一次计数器的设置,这样循环下去就可以。

        7.uart

                1.串口通信

嵌入式系统中的通信是指两个或两个以上的主机之间的数据互交,这里的主机可以是计算机也可以是嵌入式主机,甚至可以是芯片。主机间通信的方式一般可以分为两类:并行通信和串行通信。并行通信是指多个比特同时通过并行线进行传输,这种方式的传输速率较高,但会占用大量的芯片资源;串行通信是指将数据拆分成一个个比特,按照先后次序在一根总线上进行发送,串行通信有着系统占用资源少,结构简单等优点,是主机间通信的常用方式。串口通信(Serial Port)是串行通信的一种,属于串行通信中的异步通信。我们经常听到的RS232、RS485、RS422都是串行通信。

  1. 单工模式(Simplex Communication):主机间通信时如果一方固定为发送端另外一方固定为接收端,通过一根总线实现数据通信。这种通信方式就像是你只能听别人说话,但无法回答他们一样,只能单向传递信息。
  2. 半双工通信(Half-Duplex Communication)是一种通信方式,其中数据传输可以在两个方向之间交替进行,但不能同时进行。换句话说,通信双方可以既发送数据又接收数据,但不能同时进行这两种操作。比方说,就像你可以和别人交替说话和倾听对方说话一样。当你在说话时,对方在听你说;当对方在说话时,你在倾听对方。这种方式允许双方之间在发送和接收数据之间切换,但不能同时进行。半双工通信常用于对话式交流和一些简单的通信场景中。
  3. 全双工通信(Full-Duplex Communication)是一种通信方式,其中数据传输可以同时在两个方向进行,允许通信双方同时发送和接收数据,实现双向通信。就像打电话一样,你可以同时说话也可以听对方说话,双方可以同时进行数据传输,实现双向沟通。

  1. 作为常用的串行通信方式,以TTL为例,串口通信在不同主机之间的数据格式为:

  1. 空闲时数据线为高电平;
  2. 发送发发送一个低电平表示起始位;
  3. 发送的第一个比特是最低为(最右边);
  4. 校验位分为奇校验,偶校验和无校验。奇校验是指确保数据位加上校验位中"1",1的总数为奇数;偶校验是指确保数据位加上校验位中"1",1的总数为偶数;
  5. 为保证下一个字节发送前的起始位能够表现出来,校验位之后发送一个停止位1。

  1. 数据的传输速率问题:

很明显上图的纵坐标为电压值,横坐标就是时间了。无论起始位、数据为还是停止位、校验位,每个比特在数据线上的时间决定了数据传输的速率。串行通信用波特率(bit per second)来描述数据传输的速率,记作bps。常见的波特率有1200、2400,4800,9600,115200等,表示每秒钟传输的比特数。以9600为例,表明每秒能传输9600个比特。每个比特传输时所需的时间为1/9600秒=1.041*10-4秒。

  1. 同步通信和异步通信

串口通信时,收发双方的波特率必须是事先约定好的,否则数据传输就会出现混乱。很明显,为保证每个比特占用数据线的时间,发送方和接收方需按照各自的系统计时且双方之间的误差不能太大。通常不能超过(6%)。这种双方各自“计时”的方式称为异步。就好比在打字一样,每次敲击键盘发送一个字母,速度快慢由打字者自己控制,没规定每敲击一个字母之间要等多久,只要保证接收端能够正确识别并解析即可。同步通信设备之间除了有数据线以为还有一条时钟线(SDA和SCL)。其中SCL就是时钟线(serial clock)。发送方负责控制时钟线的变化,每发送一个比特,都需要将时钟线按照规则进行改变。就好比在合唱团里,大家一起唱歌的节奏是由指挥员指挥的,每个人都按照指挥员的节奏唱歌,保证大家唱的是同一首歌且节奏一致。这种通信方式就称为同步通信。譬如IIC、SPI等。

  1. 主机间通信时的电器物理问题

主机间通信无论采用并行还是串行方式,都无法避免一个物理现象:导线内阻不为零造成的电压衰减。以之前讨论的TTL电平为例,主机之间的距离会造成高电平在接收端出现衰减现象和串扰(指不同信号之间相互干扰导致信号失真)影响。

    1. TTL(Transistor-Transistor Logic)通常指的就是芯片引脚产生的电压,这个电压值跟选择的芯片有关,在51单片机系统下是5v;在2440下是3.3v等等。5vTTL通信距离通常被限制在10~20米之间,如果需要更远的距离,怎么解决呢?
    2. 为解决这个问题IEEE(Institute of Electrical and Electronics Engineers)颁布了RS232标准,其中规定了:

逻辑高电平(逻辑1):在-3V到-15V之间

逻辑低电平(逻辑0):在+3V到+15V之间

收发主机间有三根线,分别是收、发和地,因此RS232是全双工的。

理论上RS232能够传输20~30米。

  1. 同理RS485使用两根信号线(A和B)来传输数据,通过比较A和B之间的电压差来识别信息,电压范围分别为+7V到+12V和-7V到-12V。正电压表示高电平,负电压表示低电平。这种差分信号传输方式提高了抗干扰能力。RS485的传输距离可达1200米,适用于大范围的数据传输需求。由于采用的是压差,RS485在传输数据的某一时刻,两根线都要用到,所以它是半双工的。

        2.寄存器

        首先 SM0, SM1设置为 0 ,1使工作方式1,无校验位是8位。

        REN设置为1,允许串行接收控制位,也就是可以接收信息。

        SMOD置为1,

        SMOD置为0,使得SCON寄存器中的SMO/FE位于SM0功能。

        这里使用定时器1,配置定时器1

        GATA和C/T设置为0

        M1,M0为1,0 自动重装载,当高位溢出变成0后,不用管中断请求,这里只是用作设置波特率的基准,高位溢出后变成零,系统会自动把低位复制给高位,这里刚开始赋值的时候,高8位和低8位设置为一样的,这样每次都会从相同的基数开始自增到溢出。

TR1置1

        EA:置1,允许中断

        ES置1,允许串口中断

        3.代码

uart.c

#include "uart.h"
#include <reg52.h>

void init_uart(void)
{
	SCON &= ~(3 << 6);
	SCON |= (1 << 6) | (1 << 4);

	PCON &= ~(1 << 6);
	PCON |= (1 << 7);

	TMOD &= ~(0x0f << 4);	
	TMOD |= (2 << 4);
	TH1 =	232;
	TL1 = 	232;
	TCON |= (1 << 6);

	IE |= (1 << 7) | (1 << 4);
}

xdata char rev_buf[32] = {0};
unsigned int pos = 0;
void uart_handler(void) interrupt 4
{
	if((SCON & (1 << 0)) == 1)
	{
//		P2 = SBUF;
//		SCON &= ~(1 << 0);
		if(pos < 32)
		{
			rev_buf[pos++] = SBUF;
			rev_buf[pos] = 0;
		}	
		SCON &= ~(1 << 0);
	}
}

void send_char(char ch)
{
	SBUF = ch;
	while((SCON & (1 << 1)) == 0);
	SCON &= ~(1 << 1);
}

void send_buffer(const char *p,unsigned int len)
{
	while(len--)
	{
		send_char(*p++);
	}
}

void send_str(const char *p)
{
	while(*p)
	{
		send_char(*p++);	
	}
}

main.c

#include "uart.h"
#include <reg52.h>
#include "delay.h"
#include <string.h>

int main(void)
{
	//const char *s = "hello world!";
	//char buf[] = {0xaa,0xbb,0xcc,0xdd};
	init_uart();
	while(1)
	{
		
		if(pos != 0)
		{
			Delay(0xffff);
			if(strcmp(rev_buf,"ma yi chen") == 0)
			{
				send_str("lu guan!");
			}
			else if(strcmp(rev_buf,"lan bo yao") == 0)
			{
				send_str("jian shen!");
			}
			
			pos = 0;
		}
		
//		send_char('A');
//		Delay(0xffff);
//		send_buffer(s,strlen(s));
//		Delay(0xffff);
//		send_buffer(buf,sizeof(buf)/sizeof(*buf));
//		Delay(0xffff);
//		send_str(s);
//		Delay(0xffff);


//		if(pos != 0)
//		{
//			Delay(0xffff);
//			send_buffer(rev_buf,pos);
//			//send_str(rev_buf);
//			
//			pos = 0;
//		}
	}




}

        8.ds18b20

        ds18b20是半双工串行

        当开发板和ds18b20在高电平属于释放总线,可以认为都不占用传输线

                1.时序图

                        1.复位存在

        首先要进行检测ds18b20是否正常工作,开发板拉低480us~960us范围的时间,然后拉高,此时ds18b20如果能在60~240us拉低并且之后再拉高代表能正常工作。

                        2. 写0,1

 

        先拉低,保持15~60us,表示写0,最后拉高释放总线。

        先拉低保持>1us,然后再拉高,保持15~60us表示写1。

                        3.读0,1

         开发板先拉低>1us,然后拉高释放总线,保持<15us,然后读取,为0或者为1,保持高电平0~15us。 

                2.代码

ds18b20.c

#include "ds18b20.h"
#include "delay.h"
#include <reg52.h>
#include <intrins.h>

#define DQ_SET (P3 |= (1 << 7))   //P37��������
#define DQ_CLEAR (P3 &= ~(1 << 7)) //P37��������
#define DQ_TST ((P3 & (1 << 7)) != 0)//���ߵͣ�1

int ds18b20_reset(void)
{
	int t;
	DQ_CLEAR;
	Delay10us(70);

	DQ_SET;
	Delay10us(5);

	t = 0;
	while(DQ_TST && t < 30)
	{
		Delay10us(1);
		++t;	
	}
	if(t >= 30)	//ds18b20û���׹���������
	{
		return 0;
	}

	t = 0;
	while(!DQ_TST && t < 30)
	{
		Delay10us(1);
		++t;	
	}
	if(t >= 30)	//ds18b20��ʱû���߹���������
	{
		return 0;
	}
	
}

static void write_ds18b20(unsigned char dat)
{
	int i = 0;
	for(i = 0;i < 8;++i)
	{
		if(dat & (1 << i))
		{
			DQ_CLEAR;
			_nop_();
			_nop_();
			DQ_SET;
			Delay10us(5);
		}
		else
		{
			DQ_CLEAR;
			Delay10us(5);
			DQ_SET;	
		}
	}
}

static unsigned char read_ds18b20(void)
{
	unsigned char ret = 0;
	int i = 0;
	for(i = 0;i < 8;++i)
	{
		DQ_CLEAR;
		_nop_();
		_nop_();
		DQ_SET;
		_nop_();
		_nop_();
		_nop_();
		if(DQ_TST)
		{
			ret |= (1 << i);//ret�����ʱ���ֵΪ0��ֻ��Ҫ��1��ֵ����
		}
		Delay10us(5);
	}
	
	return ret;
}

float get_temprature(void)
{
	unsigned char tl;
	unsigned char th;
	short ret;
	ds18b20_reset();
	write_ds18b20(0xCC);
	write_ds18b20(0X44);
	Delay1ms(1000);
	ds18b20_reset();
	write_ds18b20(0xCC);
	write_ds18b20(0XBE);
	tl = read_ds18b20();
	th = read_ds18b20();
	ret = tl;
	ret |= th << 8;
	return ret * 0.0625;
}

读取的结果是俩个字节,定义为shart类型,高八位和低八位。

            unsigned char tl;
            unsigned char th;
            short ret;

低八位直接赋值,高八位将读取的数值左移8位,移到高八位的位置然后或运算。

            ret = tl;
            ret |= th << 8;

因为读取的结果包括小数点后四位,所以精度就是0.0625,用读取的结果*0.0625就是实际的温度。

        return ret * 0.0625

       main.c

#include "delay.h"
#include "ds18b20.h"
#include <reg52.h>
#include <stdio.h>
#include "uart.h"
#include "digiter.h"

int main(void)
{
	float t;
	char s[24];
	int i = 1000;
	init_uart();
	while(1)
	{
		t = get_temprature();
		sprintf(s,"%f",t);
		send_str(s);
		 
		while(--i)
		{
		   display((unsigned int)t);
		}
		i = 1000;

	}
	
}


网站公告

今日签到

点亮在社区的每一天
去签到