2018-07-15 14:37:55 qq_36739040 阅读数 1478
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

C51单片机————串行接口


Created with Raphaël 2.1.2张三张三李四李四嘿,四哥,我最近在学习串行接口?想了想!有我啥事嗯!牛逼我有点不懂?哪里不懂哇额! 都不懂。。。

1.引文

计算机和外界的信息交换叫做通信。

串行和并行

基本的通信方式分为2种:串行和并行
串行通信:这种通信方式发送/接收数据是一位一位按顺序(bit)的进行传输的(相当于公路的单行道吧!)

传输线多(每次多传一位就多一根线)、价格贵、适合近距离的传输、传输速度快

并行通信:发送/接收数据可以是多位同时传输的(到底是多少位一起传输,要看硬件设备了)

传输线只需要1到2根就可以、便宜、远距离传输、但是速度慢(别人一次传几个,你一次传一个你说慢不慢)


同步和异步

这个怎么区分呢?
根据帧信息的格式区分的!
同步:我的理解就是通信的双方拥有相同的时间线,严格按照此时间线进行通信
1. 通信的数据格式是 :同步字符–连续的数据–校验字节
2. 速度高于异步,硬件结构高
异步:可以看做不同时间轴的操作
1. 通信的数据格式是 :一帧数据为11位的帧格式:起始位–数据8位–校验位-停止位 (1帧==11位)

通俗点的理解:
大家都买过衣服吧!在商场去买和在某宝上买就像同步和异步操作。
在商场买衣服,需要选衣服,选了衣服后让服务员帮你包好,然后结束自己带回家,这个过程是连续的。
在某宝上呢?你只是需要选衣服、下单然后就等着收快递了,虽然衣服还没有送到家,但是你买衣服的任务已经完成了。商家接到你的订单后,就会加紧安排送货,当然这一切已经跟你无关了,你已经支付完成,想什么就能去干什么了。
额。。。好像也不通俗。。。


通信方向

单工传送:单行道,数据只可以朝着一个方向!
半双工:可以双向传送的车道,但是两个方向不可同时使用(不能同时发送接收)
全双工:双向传送的车道,可同时发送接收。

2.串行通信的任务

Created with Raphaël 2.1.2李四李四张三张三CPU处理什么数据的?知道吗额,并行数据对了!那他怎么处理串行数据呢?对啊!怎么处理啊?以手蒙面状。。。

串行接口就在这儿起作用了?
CPU处理并行数据的,让他处理串行数据的话就需要串行接口的帮忙了!CPU还要遵守串行通信的协议(通信双方都需要遵守的约定,约定有:数据格式、同步方式、传送步骤、检查/纠错的方式、控制符的定义。。。)

串行接口的任务:

  1. 数据的处理(不同的串行通信有不同的数据格式)
  2. 并行数据和串行数据的转换
  3. 数据的传输速度
  4. 传输的错误检测(奇偶校验位、校验码。。。)

串行接口的类型:

串行通信接口Serial Communication Interface:设备之间互联接口,互相之间距离较长

串行扩展接口Serial Expand Interface: SPI IIC。。。

Baud rate | bps | Band width:

Baud rate波特率:单位时间里传输的二进制代码有效位数

bps比特率:单位时间里传输的码元个数

Band width带宽:
1. 模拟信号中:带宽是信号所占频率
2. 数字信号里:带宽是传送数字信号速率

单片机中波特率和比特率数值上相同(一个状态对应一个1或0)


3.单片机串行口结构和工作原理

51单片机的串行口:可编程、全双工。
1. 可作为UART(Universal Asynchronous Receiver/Transmitter)
2. 同步移位器
3. 帧格式:8位、10位、11位
4. 可设置波特率

这里写图片描述

SBUF:发送和接收的数据缓冲区
SOCN:串行口控制寄存器 (98H)
PCON:电源控制寄存器 (87H)
TI:发送中断标志位 (方式0:发送结束硬件置1 发送前软件置0)
RI:接收中断标志位 (方式0:发送结束硬件置1 发送前软件置0)
TXD: P3.1引脚,发送数据口
RXD: P3.0引脚,接收数据口

发送和接收SBUF可同时发送接收(它们在物理上独立的)但是它们公用 SFR地址99H。
发送时:data 写入SBUF ,经过TXD发出
接收时:存储RXD接收到的数据


(1)SCON 串行口控制寄存器(地址98h)

SCON (98H)
SM0
SM1 SM2
REN TB8
RB8 TI
RI
方式选择
多机通信控制
串行接收允许 欲发的第九位
欲收的第九位 发送中有无中断
接收中有无中断
有四种方式0、1、2、3
方式2、3时候用
0禁止、1允许 方式2、3中
方式2、3中 软件清零 硬件置一
软件清零 硬件置一

(2)PCON 串行口控制寄存器(地址87h)

PCON (78H)
SMOD
X X
X GF1
GF0 PD
IDL
波特率加倍位
X X
X 通用标志位 掉电方式位 待机方式位
1加倍、0不加倍
X X
X 用户可作为软件使用标志 1激活掉电工作方式 1激活待机工作方式

4.单片机串行口工作方式


方式0:

方式0 :移位寄存器IO方式
TXD:移位数据IO
RXD:移位时钟IO
波特率 = fosc / 12
8位 数据为一帧,不设置其实和停止位,从最低位开始发送
发送过程:(1).写SBUF(2).8位数据发完,TI=1(3).发送下一数据
接收过程:(1).REN=1(2).8位数据收完,RI=1(3).读SBUF
功能:(1)短距离单片机之间数据传输(2)扩展输出接口 。。。

方式1:

方式1:波特率可变10位异步通信方式
TXD:串行数据发送端
RXD:串行数据接收端
波特率 :
baudrate=2SMOD32(T1)
波特率 :
baudrate=2SMOD32fosc12256X
>> X为T1的计数初值 T1工作在方式2(这是计数器的工作方式哈)
10位 数据为一帧,从最低位开始发送 起始位和停止位是自动插入的
使用了T1计数器

方式2:

方式2:波特率固定11位异步通信方式
TXD:串行数据发送端
RXD:串行数据接收端
波特率固定 :
baudrate=2SMODfosc64
11位 数据为一帧,从最低位开始发送
起始位和停止位是自动插入的
第九个数据是TB8、RB8(分别是发送和接收)位提供的

方式3:

方式3:波特率可变11位异步通信方式 (数据格式同方式2、波特率计算同方式1)
TXD:串行数据发送端
RXD:串行数据接收端
波特率可变同方式1 :
baudrate=2SMOD32fosc12256X
>> X为T1的计数初值 T1工作在方式2(这是计数器的工作方式哈)
11位 数据为一帧,从最低位开始发送
起始位和停止位是自动插入的
第九个数据是TB8、RB8(分别是发送和接收)位提供的

5.单片机串行口编程

编程要点:
1. 波特率计算
2. 控制字填写
3. 两种工作方式选择(查询和中断)
4. 两方波特率一致,可约定某一个标志字符作为发送数据的开始(多机通信中,标志字符就是各分机的地址)


  • 查询方式:
    发送程序:发数据— 查询TI — 发送下一个数据(先发后查)
    接收程序:查询RI—读数据—查询RI—读下一个数据(先查后收)
  • 中断方式:
    发送程序:发数据—等待中断 — 发送下一个数据
    接收程序:等待中断,在中断中再接收下一个数据

两种方式中都要注意清TI、RI


ORG 0000H
AJMP MAIN
ORG 0030H
MAIN: 
    MOV TMOD,#20H ;TMOD设置  T1方式二
    MOV TH0 ,#3cH;
    TL0 = #0b0H;  ;初值
    MOV SCON,#40H  ;SCON设置 串行方式1
    MOV R0,#20h    ;发送的数据缓冲区起始地址
    MOV R7,#32     ;发送的数据个数
    SETB TR1      ;启动定时器,TRx = 1
    SETB EA     ;中断总开关
    SETB ET1    ;定时器0  允许中断
    LOOP:
    MOV SBUF,@R0;发送的数据放到缓冲区
    JNB TI,$    ;  TI==0 死循环
    CLR TI      ;软件清零
    INC R0      ;下一个数据
    DJNZ R7,LOOP;是否发完
HERE:AJMP HERE
END
ORG 0000H
AJMP MAIN
ORG 0030H
MAIN: 
    MOV TMOD,#20H ;TMOD设置  T1方式二
    MOV TH0 ,#3cH;
    TL0 = #0b0H;  ;初值
    MOV SCON,#50H  ;串行方式1   可接收REN
    MOV R0,#20h    ;发送的数据缓冲区起始地址
    MOV R7,#32     ;发送的数据个数
    SETB TR1      ;启动定时器,TRx = 1
    SETB EA     ;中断总开关
    SETB ET1    ;定时器0  允许中断
    LOOP:JNB RI,$    ;RI ==0 死循环 
    CLR RI      ;软件清零
    MOV @R0,SBUF;发送的数据放到缓冲区

    INC R0      ;下一个数据
    DJNZ R7,LOOP;是否发完
HERE:AJMP HERE
END

如有错误!请多多包含,方便的话留个言!我好马上修改。

2018-11-29 14:49:34 niuyuce 阅读数 210
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

80C51的串行口的结构

SBUF:两个物理上独立的接收、发送缓冲器。他们两个占用同一个地址99H。接收器是双缓冲结构,发送缓冲器,因为发送时,CPU是主动的,不会产生重叠错误。

 

80C51的串行口的控制寄存器

1.SCON是一个特殊功能寄存器,用来设定串行口的工作方式接收/发送控制以及设置状态标志

SM0和SM1是工作方式选择位(和之前一样),可以选择四种工作方式,如下

SM0、SM1 工作方式 说明 波特率
00 方式0 移位寄存器 12
01 方式1 10位异步收发器(8位数据) 可变
10 方式2 11位异步收发器(9位数据) 64或32
11 方式3 11位异步收发器(9位数据) 可变

 

RI(SCON.0),接收中断标志位。在方式0时,当串行接收第8位数据结束时;或在其他方式,串行接收停止位中间时,由内部硬件使RI置1,向CPU发出中断申请。在中断服务程序中,用软件将其清0,取消此中断申请。

TI(SCON.1),发送中断标志位。在方式0时,当串行发送第8位数据结束时;或在其他方式,串行发送停止位中间时,由内部硬件使TI置1,向CPU发出中断申请。在中断服务程序中,用软件将其清0,取消此中断申请。

RB8(SCON.2),在方式2或方式3中,是接受到数据的第九位,可以用软件规定起作用,可以用作数据的奇偶校验位。在多机通信中,作为地址帧、数据帧的标志位。在方式1时,若SM2=0,则RB8是接收到的停止位。

TB8(SCON.3),在方式2或方式3中,是发送数据的第九位,可以用软件规定起作用,可以用作数据的奇偶校验位。在多机通信中,作为地址帧、数据帧的标志位。在方式0和方式1中,该位未用。

REN(SCON.4),允许串行接受位。由软件置REN=1,则启动串行口接收数据。若软件置REN=0,则禁止接收。

SM2(SCON.5),多机通信控制位。主要用于方式2和方式3,当接收机的SM2=1时,可以利用收到的RB8来控制是否激活RI。(RB8=0时,不激活RI,收到的信息丢弃;RB8=1时,激活RI,收到的数据进入SBUF,进而在中断服务中将数据从SBUF中读走)。当接收机的SM2=0时,不论收到的RB8为0和1,均可以是收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。在方式0时,SM2必须是0。在方式1时,若SM2=1,则只有接收到有效停止位时,RI才置1。

 

2.PCON中只有一位SMOD与串行口工作有关,如下

SMOD(PCON.7),波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,比特率增加一倍。复位时,SMOD=0。

 

 

2011-06-01 13:00:00 s_a_n_ 阅读数 1833
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏
#include <REGX52.H>

//===================================================================
//文件名:serial_traveler.h 
//  51单片机 串行口数据传输接口说明:
//	串行收发数据需要占用定时器1 和串行口 TXD、RXD
//	接口中定义了忙标志 busy_serial 1表示接口被此接口占用,0表示未被此接口占用
//	此接口中 串口工作方式1、8位异步收发、允许接收数据、需要接收有效的停止位
//	如需其它工作方式,请自行修改
//	挂起串口时,释放定时器 和 串口,串口失去时钟源,任何读 或 写串口的操作都会失败
//	如有指教,请联系我:759981398@qq.com 
//==================================================================

#ifndef _SERIAL_TRAVELER_H_
#define _SERIAL_TRAVELER_H_
#define _TRUE_TRAVELER_ 1
#define _FALSE_TRAVELER_ 0

//==================================================================
//函数名:  serialStartup()
//功能:    初始化串口
//输入参数:无
//返回值:  无
//其他: 	同时占用定时器1
//==================================================================
void serialStartup();

//==================================================================
//函数名:  serialClose()
//功能:    关闭串口
//输入参数:无
//返回值:  无
//其他: 	同时关闭定时器1
//==================================================================
void serialClose();

//==================================================================
//函数名:  serialSelectBode
//功能:    设定串口波特率
//输入参数:波特率值/100
//返回值:  操作成功返回 _TRUE_TRAVELER_ ;操作失败返回 _FALSE_TRAVELER_
//其他: 	无
//==================================================================
char serialSelectBode(unsigned char bodeValue);

//==================================================================
//函数名:  serialRun()
//功能:    串口开始工作
//输入参数:无
//返回值:  无
//其他: 	占用定时器T1
//==================================================================
#ifndef serialRun
#define serialRun() TR1 = 1 
#endif

//==================================================================
//函数名:  serialClear()
//功能:   清除溢出标志位,为下一次传输做准备
//输入参数:无
//返回值:  无
//其他: 	无
//==================================================================
#ifndef serialClear
#define serialClear() TI = 0; RI = 0
#endif

//==================================================================
//函数名:  serialPauseResume
//功能:    挂起/恢复 串口
//输入参数:操作码 _TRUE_TRAVELER_~挂起串口;_FALSE_TRAVELER_~恢复串口
//返回值:  无
//其他: 	挂起后 可以解除定时器绑定 串口的sfr也可以修改作其他用处
//			恢复后串口可像挂起前一样正常工作
//==================================================================
void serialPauseResume(char opcode);

//==================================================================
//函数名:  serialWriteChar
//功能:    写字符到串口
//输入参数:待发送的字符
//返回值:  _FALSE_TRAVELER_~发送失败  /// _TRUE_TRAVELER_ ~ 发送成功
//其他: 	无
//==================================================================
char serialWriteChar(unsigned char chr);

//==================================================================
//函数名:  serialReadChar
//功能:    从串口读取1个字符
//输入参数:接收字符的单元地址
//返回值:  _FALSE_TRAVELER_~接收失败  /// _TRUE_TRAVELER_ ~ 接收成功
//其他: 	无
//==================================================================
char serialReadChar(unsigned char *chr);

//==================================================================
//函数名:  serialWriteString
//功能:    写字符串到串口
//输入参数:str 字符串首地址,应注意使字符串结束的字节为'\0'
//返回值:  返回发送成功的字符数
//其他: 	无
//==================================================================
char serialWriteString(const unsigned char *str);

//==================================================================
//函数名:  serialReadString
//功能:    从串口读取字符串
//输入参数:str 字符串首地址 ,len 允许接收的最大字节数
//返回值:  返回接收到的字符数
//其他: 	无
//==================================================================
char serialReadString(unsigned char *str,unsigned char len);

//==================================================================
//函数名:  serialWriteInt
//功能:    以字符串格式向uart发送一个整数
//输入参数:int dat 待发送的整数值
//返回值:  int 成功发送的字符个数
//其他: 	返回值若为-1 表示数据处理阶段发生错误、未发送
//==================================================================
char serialWriteInt(int dat);

#endif

 

/*
文件名:serial_traveler.c 
*/
#include <REGX52.H>
#include "serial_traveler.h"

//下表为11.0592MHz 晶振下 部分波特率对应定时器1自动重载计数初值、smod位设置 适用于串行口的方式1 和 方式3
const unsigned char bode_serial[][3]= 
{
	{192,0xfd,1},// 19.2 kb/s - 初值 0xfd  - SMOD = 1
	{96 ,0xfd,0},// 9.6  kb/s - 初值 0xfd  - SMOD = 0
	{48 ,0xfa,0},// 4.8  kb/s - 初值 0xfa  - SMOD = 0
	{24 ,0xf4,0},// 2.4  kb/s - 初值 0xf4  - SMOD = 0
	{12 ,0xe8,0} // 1.2  kb/s - 初值 0xe8  - SMOD = 0
};

//busy_serial指示是否已占用了串口和定时器1
static char busy_serial = _FALSE_TRAVELER_ ; 

//==================================================================
//函数名:  serialStartup()
//功能:    初始化串口
//输入参数:无
//返回值:  无
//其他: 	同时占用定时器1
//==================================================================
void serialStartup()
{
	busy_serial = 1;
	TMOD &= 0x0f;//清空对定时器1的设置
	TMOD |= 0x20;//定时器1方式2 作波特率发生器
	TCON &= 0x3f;//关定时器1
	SCON  = 0x70;//设置串口 方式1 8位异步收发 允许接收数据 需要接收有效的停止位
}

//==================================================================
//函数名:  serialClose()
//功能:    关闭串口
//输入参数:无
//返回值:  无
//其他: 	同时关闭定时器1
//==================================================================
void serialClose()
{
	TMOD &= 0x0f;//清空对定时器1的设置
	TR1   = 0;//关定时器1
	busy_serial  = 0;
}

//==================================================================
//函数名:  serialSelectBode
//功能:    设定串口波特率
//输入参数:波特率值/100
//返回值:  操作成功返回 _TRUE_TRAVELER_ ;操作失败返回 _FALSE_TRAVELER_
//其他: 	无
//==================================================================
char serialSelectBode(unsigned char bodeValue)
{
	char c;
	for(c = 0;c!=5;++c)
	{
		if(bode_serial[c][0] == bodeValue)
		{
			TH1 = bode_serial[c][1];
			TL1 = bode_serial[c][1];
			PCON &= 0x7f;//SMOD位清零
			PCON |= (bode_serial[c][2]<<7);//设置SMOD位
			return _TRUE_TRAVELER_;
		}
	}
	return _FALSE_TRAVELER_;
}


//==================================================================
//函数名:  serialRun()
//功能:    串口开始工作
//输入参数:无
//返回值:  无
//其他: 	占用定时器T1
//==================================================================
#ifndef serialRun
#define serialRun() TR1 = 1 
#endif

//==================================================================
//函数名:  serialClear()
//功能:   清除溢出标志位,为下一次传输做准备
//输入参数:无
//返回值:  无
//其他: 	无
//==================================================================
#ifndef serialClear
#define serialClear() TI = 0; RI = 0
#endif

//==================================================================
//函数名:  serialPauseResume
//功能:    挂起/恢复 串口
//输入参数:操作码 _TRUE_TRAVELER_~挂起串口;_FALSE_TRAVELER_~恢复串口
//返回值:  无
//其他: 	挂起后 可以解除定时器绑定 串口的sfr也可以修改作其他用处
//			恢复后串口可像挂起前一样正常工作
//==================================================================
void serialPauseResume(char opcode)
{	
	static char th1,tl1,tmod_4567,scon,pcon_7,tcon_67;
	if(opcode == _TRUE_TRAVELER_ )//挂起操作 保护现场
	{
		th1 = TH1;
		tl1 = TL1;
		tmod_4567 = TMOD & 0XF0;
		scon = SCON;
		pcon_7 = PCON & 0X80;
		tcon_67 = TCON & 0XC0;
		TR1 = 0;
		busy_serial = 0 ;
	}
	else//恢复 恢复现场
	{
		TH1 = th1;
		TL1 = tl1;
		TMOD &= 0X0F;
		TMOD |= tmod_4567;
		SCON = scon;
		PCON &= 0X7F;
		PCON |= pcon_7;
		TCON &= 0X3F;
		TCON |= tcon_67;
		busy_serial = 1;
	}
}

//==================================================================
//函数名:  serialWriteChar
//功能:    写字符到串口
//输入参数:待发送的字符
//返回值:  _FALSE_TRAVELER_~发送失败  /// _TRUE_TRAVELER_ ~ 发送成功
//其他: 	无
//==================================================================
char serialWriteChar(unsigned char chr)
{	
	if(_FALSE_TRAVELER_ == busy_serial)//串口未启用 退出
		return _FALSE_TRAVELER_;
	if(RI)//正在接收数据 退出
		return _FALSE_TRAVELER_;
		
	SBUF = chr;
	while(!TI);
	TI = 0;
	return _TRUE_TRAVELER_;
}

//==================================================================
//函数名:  serialReadChar
//功能:    从串口读取1个字符
//输入参数:接收字符的单元地址
//返回值:  _FALSE_TRAVELER_~接收失败  /// _TRUE_TRAVELER_ ~ 接收成功
//其他: 	无
//==================================================================
char serialReadChar(unsigned char *chr)
{
	if(_FALSE_TRAVELER_ == busy_serial)//串口未启用 退出
		return _FALSE_TRAVELER_;
	if(TI) //正在发送数据 退出
		return _FALSE_TRAVELER_;
	while(!RI);
	RI = 0;
	*chr = SBUF;
	return _TRUE_TRAVELER_;
}

//==================================================================
//函数名:  serialWriteString
//功能:    写字符串到串口
//输入参数:str 字符串首地址,应注意使字符串结束的字节为'\0'
//返回值:  返回发送成功的字符数
//其他: 	无
//==================================================================
char serialWriteString(const unsigned char *str)
{
	char c = 0;
	for(c = 0;str[c]!=0;c++)
	{//不到字符尾则继续发送
		if(_FALSE_TRAVELER_ == serialWriteChar(str[c]))	
			break; //发送失败则终止发送
	}
	return c;
}

//==================================================================
//函数名:  serialReadString
//功能:    从串口读取字符串
//输入参数:str 字符串首地址 ,len 允许接收的最大字节数
//返回值:  返回接收到的字符数
//其他: 	注意您的字符数组应预留'\0'字符的位置 即至少数组应含有len+1个元素
//==================================================================
char serialReadString(unsigned char *str,unsigned char len)
{ 
	unsigned char c = 0;
	while(c<len)
	{
		if((_FALSE_TRAVELER_ == serialReadChar(&str[c]))||str[c] =='\r' || str[c] == '\n')
		{ //如果接收字符失败或接收到的字符是回车键则终止接收字符	
			
			str[c] = 0;
			c++;
			break;
		}
		c++; //接收下一个字符
	}
	return c;
}

//==================================================================
//函数名:  serialWriteInt
//功能:    以字符串格式向uart发送一个整数
//输入参数:int dat 待发送的整数值
//返回值:  int 成功发送的字符个数
//其他: 	返回值若为-1 表示数据处理阶段发生错误、未发送
//==================================================================
char serialWriteInt(int dat)
{
	//从int型到字符数组之间的转换
	unsigned char str[11]= {0,0,0,0,0,0,0,0,0,0,0};
	char dat_tmp;
	char pos ;
	char neg = 0;
	str[10] = 0;
	pos = 10 ;
	
	if(dat<0)//处理负数 上
	{
		dat = - dat;
		neg = 1;
	}
	
	do//循环 转换数据格式
	{
		dat_tmp = dat % 10 ;
		str[--pos] = '0' + dat_tmp ;
		dat /= 10 ;
	}
	while(dat != 0) ;
	
	if(neg)//处理负数 下
	{
		str[--pos] = '-';
	}
	
	if(pos <0)//出错处理
		return -1 ;
		
	return	serialWriteString((unsigned char *)&str[pos]);
}



 

2016-05-27 18:05:58 sinat_30685475 阅读数 3465
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏

一、例程简介

    本例程51单片机与蓝牙模块连接,可通过蓝牙模块接收和发送字符串,从而控制测试灯的亮灭。其中使用51单片机的串行口2的工作方式1,即8位UART,波特率可变。波特率设为9600。缺省UART2在P1口。

    测试程序实现的功能:

    1、蓝牙模块接收到“0”~“6”字符串时,分别实现LED0~4的不同亮灭效果;

    2、执行字符串“6”对应效果后,通过蓝牙模块发送字符串“\rHello!”到模块连接的蓝牙设备。


二、硬件部分

C51芯片:STC12C5A60S2 PDIP-40

蓝牙模块:HC-05

晶振:11.0592MHz

-- 连接电路 --

最小系统

51单片机最小系统

(测试用BST-V51 51单片机学习板)

蓝牙模块

+5V 接 单片机VCTC

GND 接 单片机GND

TX 接 P1.2/RxD2

RX 接 P1.3/TxD2

其它引脚悬空


三、软件部分

-- C语言代码 --

#include "reg51.h"
#include "intrins.h"
#include "string.h"

typedef unsigned char uchar;
typedef unsigned int uint;

#define FOSC 11059200L //System frequency
#define BAUD 9600 //UART baudrate
#define PARITYBIT NONE_PARITY //Testing none parity

/*Define UART parity mode*/
/* Copy from STC12C5A60S2 Data Sheet*/
#define NONE_PARITY 0 //None parity
#define ODD_PARITY 1 //Odd parity
#define EVEN_PARITY 2 //Even parity
#define MARK_PARITY 3 //Mark parity
#define SPACE_PARITY 4 //Space parity

/*Declare SFR associated with the UART2 */
/* Copy from STC12C5A60S2 Data Sheet*/
sfr AUXR = 0x8e; //Auxiliary register
sfr AUXR1 = 0xa2;
sfr S2CON = 0x9a; //UART2 control register
sfr S2BUF = 0x9b; //UART2 data buffer
sfr BRT = 0x9c; //Baudrate generator
sfr IE2 = 0xaf; //Interrupt control 2
#define S2RI 0x01 //S2CON.0
#define S2TI 0x02 //S2CON.1
#define S2RB8 0x04 //S2CON.2
#define S2TB8 0x08 //S2CON.3

/* Define variables associated with the UART2*/
char UART_buff;
char Str[16];
uchar j = 0;

/*Define pin of LED for testing*/
sbit LED0 = P1^0;
sbit LED1 = P1^4;
sbit LED2 = P1^5;
sbit LED3 = P1^6;
sbit LED4 = P1^7;

// delay for x ms
void delayxms(uint x)
{
	uint i;
  uchar a,b;
	for(i=0;i<x;i++)
    for(b=18;b>0;b--)
        for(a=152;a>0;a--) ;
}

/* Copy from STC12C5A60S2 Data Sheet*/
void Uart2_Init()
{
	#if (PARITYBIT == NONE_PARITY)
		S2CON = 0x50; //8-bit variable UART
	#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
		S2CON = 0xda; //9-bit variable UART, parity bit initial to 1
	#elif (PARITYBIT == SPACE_PARITY)
		S2CON = 0xd5; //9-bit variable UART, parity bit initial to 0
	#endif
	BRT = -(FOSC/32/BAUD); //Set auto-reload vaule of baudrate generator
	AUXR = 0x14; //Baudrate generator work in 1T mode
//	AUXR1  |= 0x10; // If needed, switch UART2 Pin from P1 to P4
	IE2 = 0x01; //Enable UART2 interrupt
	EA = 1; //Open master interrupt switch
}

/*----------------------------
Send a string to UART
Input: s (address of string)
Output:None
----------------------------*/
void SendString(char *s)
{
	int i = 0;
	int l = strlen(s);
	for(i; i<l; i++)
	{
		S2BUF = s[i];
    while(!(S2CON&S2TI)) ;
    S2CON &= ~S2TI;;
	}  
}

void main()
{
	Uart2_Init();
	
	LED0 = 1;
	LED1 = 1;
	LED2 = 1;
	LED3 = 1;
	LED4 = 1;
	
	while(1) ;
}

/*----------------------------
UART2 interrupt service routine
----------------------------*/
void Uart2() interrupt 8 using 1
{
	if (S2CON & S2RI)
	{
		S2CON &= ~S2RI; //Clear receive interrupt flag
		UART_buff = S2BUF; //UART_buff to save UART data
		
		if(UART_buff != '\0')	// When it's not the end of message,
		{
			Str[j++] = UART_buff; // Continue to record character.
		}
		else // When message ends, do the specific job
		{
			Str[j] = UART_buff;
			if(strcmp(Str, "0") == 0)
			{
				LED0 = ~LED0;
				delayxms(100);
			}
			else if(strcmp(Str, "1") == 0)
			{
				LED1 = ~LED1;
				delayxms(100);
			}
			else if(strcmp(Str, "2") == 0)
			{
				LED2 = ~LED2;
				delayxms(100);
			}
			else if(strcmp(Str, "3") == 0)
			{
				LED3 = ~LED3;
				delayxms(100);
			}
			else if(strcmp(Str, "4") == 0)
			{
				LED4 = ~LED4;
				delayxms(100);
			}
			else if(strcmp(Str, "5") == 0)
			{
				LED1 = 0;
				LED2 = 0;
				LED3 = 0;
				LED4 = 0;
				delayxms(100);
			}
			else if(strcmp(Str, "6") == 0)
			{
				LED1 = 1;
				LED2 = 1;
				LED3 = 1;
				LED4 = 1;
				delayxms(100);
				SendString("\rHello!");
			}
			strcpy(Str, "");
			j = 0;
		}
	}
	if (S2CON & S2TI)
	{
		S2CON &= ~S2TI; //Clear transmit interrupt flag
	}
}




2011-04-17 11:08:00 yanghuazhou 阅读数 10804
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3400 人正在学习 去看看 朱有鹏
51单片机外部中断的C51编程

相关知识:
1、51单片机的5大中断源:串行口中断、定时中断1、外部中断1、定时中断0、外部中断0;
2、中断源的编号: 串行口中断为4、定时中断1为3、外部中断1为2、定时中断0为1、外部中断0为0;
3、中断源的优先级:按以上顺序排列,串行口中断最低、外部中断0最高;
4、使用外部中断0和1,必须TCON寄存器设置其触发方式是低电平触发(0)还是下降沿触发(1);
5、使用前必须通过IE寄存器打开总中断和自己的中断;

//外部中断基本例程-1(未使用中断,键盘扫描为一般端口扫描)
//这是特意安排的一个例程,以便和使用外部中断的例程2进行对比
//用一个按键控制一个灯的亮灭,开始不亮,按一下则点亮,再按一下灭掉,再按又亮........
//广西民大物电学院 李映超 2010年4月12日

#include <reg52.h>
sbit k1=P3^2;
sbit led=P2^7;
void delay_ms(unsigned int xms); //ms级延时子程序
void key_scan();   //声明键盘扫描子函数
//=================================================
void main()
{
led=1; //上电初始化,led灯不亮
while(1)
{
key_scan();
delay_ms(3000);
}
}
//=================================================
void delay_ms(unsigned int xms) //ms级延时子程序
{ unsigned int x,y;
for(x=xms;x>0;x--)
for(y=130;y>0;y--);}
//-------------------------------------------------
void key_scan() //键盘扫描子函数
{ if(k1==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k1==0)     //确实是有键按下,则:
{led=!led; //翻转灯的状态
    while(!k1);} //等待按键放开
}
}
//-------------------------------------------------

 

//外部中断基本例程-2 (单个键盘的外部中断0扫描处理)
//用一个按键控制一个灯的亮灭
//开始不亮,按一下则点亮,再按一下灭掉,再按又亮........
//广西民大物电学院 李映超 2010年4月12日

#include <reg52.h>
sbit k1=P3^2;
sbit led=P2^7;
void delay_ms(unsigned int xms); //ms级延时子程序
void key_scan() interrupt 0 //使用了外部中断0的键盘扫描子函数。也可放在主函数而不需要预先声明
{ if(k1==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k1==0)     //确实是有键按下,则:
{led=!led; //翻转灯的状态
    while(!k1);} //等待按键放开
}
}
//=================================================
void main()
{
led=1;    //上电初始化,led灯不亮
TCON=0x01; //打开外部中断0,并设置为下降沿触发
IE=0x81; //开总中断

while(1)
{
delay_ms(3000); //注意主函数这里没有键盘扫描程序了
}
}
//=================================================
void delay_ms(unsigned int xms) //ms级延时子程序
{ unsigned int x,y;
for(x=xms;x>0;x--)
for(y=130;y>0;y--);}
//-------------------------------------------------

 

//外部中断基本例程-3 (单个键盘的外部中断1扫描处理)
//功能跟上例一样,都是用一个按键控制一个灯的亮灭,但是使用的是外部中断1
//注意与前例写法上的不同之处
//广西民大物电学院 李映超 2010年4月12日

#include <reg52.h>
sbit k2=P3^3;   //
sbit led=P2^7;
void delay_ms(unsigned int xms); //ms级延时子程序
//=================================================
void main()
{
led=1;    //上电初始化,led灯不亮
TCON=0x04; //打开外部中断1,并设置为下降沿触发
IE=0x84; //开总中断和外部中断1

while(1)
{
delay_ms(3000);
}
}
//=================================================
void delay_ms(unsigned int xms) //ms级延时子程序
{ unsigned int x,y;
for(x=xms;x>0;x--)
for(y=130;y>0;y--);}
//-------------------------------------------------
void key_scan() interrupt 2 //使用了外部中断1的键盘扫描子函数
{ if(k2==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k2==0)     //确实是有键按下,则:
{led=!led; //翻转灯的状态
    while(!k2);} //等待按键放开
}
}

 

//外部中断基本例程-4 (同时使用外部中断0和外部中断1)
//用一个键控制接在P0口的八个灯的亮灭,另外一个按键控制一个灯的亮灭
//广西民大物电学院 李映超 2010年4月12日

#include <reg52.h>
sbit k1=P3^2;
sbit k2=P3^3;   //
sbit led=P2^7;
void delay_ms(unsigned int xms); //ms级延时子程序
//=================================================
void main()
{
P0=0xff; //上电初始化,P0全灭
led=1;    //上电初始化,led灯不亮
TCON=0x05; //打开外部中断1,并设置为下降沿触发
IE=0x85; //开总中断和外部中断1

while(1)
{
delay_ms(3000);
}
}
//=================================================
void delay_ms(unsigned int xms) //ms级延时子程序
{ unsigned int x,y;
for(x=xms;x>0;x--)
for(y=130;y>0;y--);}
//-------------------------------------------------
void key_scan_1() interrupt 0 //使用了外部中断0的键盘扫描子函数
{ if(k1==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k1==0)     //确实是有键按下,则:
{P0=~P0; //翻转8个灯的状态
    while(!k1);} //等待按键放开
}
}
void key_scan_2() interrupt 2 //使用了外部中断1的键盘扫描子函数
{ if(k2==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k2==0)     //确实是有键按下,则:
{led=!led; //翻转单个灯的状态
    while(!k2);} //等待按键放开
}
}

 

//外部中断扩展应用例程-5(8个按钮分别去控制8个灯的亮灭)
//八个按钮通过8个二极管接到外部中断0脚进行中断扩展,
//从而解决外部中断口不够用的问题
//广西民大物电学院 李映超 2010年4月12日

#include <reg52.h>
sbit k0=P2^0; //八个按键分别接到P2口各个脚上,
sbit k1=P2^1; //同时还通过二极管连接到外部中断0脚(P3.2)
sbit k2=P2^2;
sbit k3=P2^3;
sbit k4=P2^4;
sbit k5=P2^5;
sbit k6=P2^6;
sbit k7=P2^7;

sbit led0=P0^0; //接在P0脚上的8个LED灯,分别受控于上述8个按键
sbit led1=P0^1;
sbit led2=P0^2;
sbit led3=P0^3;
sbit led4=P0^4;
sbit led5=P0^5;
sbit led6=P0^6;
sbit led7=P0^7;

void delay_ms(unsigned int xms); //ms级延时子程序
//=================================================
void main()
{
   //上电初始化,led灯不亮
TCON=0x01; //打开外部中断0,并设置为下降沿触发
IE=0x81; //开总中断

while(1)
{
delay_ms(3000);
}
}
//=================================================
void delay_ms(unsigned int xms) //ms级延时子程序
{ unsigned int x,y;
for(x=xms;x>0;x--)
for(y=130;y>0;y--);}
//-------------------------------------------------

void key_scan() interrupt 0 //使用了外部中断0的键盘扫描子函数
{ if(k0==0) //有键按下吗?(k0=0 ?)
{ delay_ms(10); //延时消抖
if(k0==0)     //确实是有键按下,则:
{led0=!led0; //翻转灯的状态
    while(!k0);} //等待按键放开
}
if(k1==0) //有键按下吗?(k1=0 ?)
{ delay_ms(10); //延时消抖
if(k1==0)     //确实是有键按下,则:
{led1=!led1; //翻转灯的状态
    while(!k1);} //等待按键放开
}
if(k2==0) //有键按下吗?(k2=0 ?)
{ delay_ms(10); //延时消抖
if(k2==0)     //确实是有键按下,则:
{led2=!led2; //翻转灯的状态
    while(!k2);} //等待按键放开
}
if(k3==0) //有键按下吗?(k3=0 ?)
{ delay_ms(10); //延时消抖
if(k3==0)     //确实是有键按下,则:
{led3=!led3; //翻转灯的状态
    while(!k3);} //等待按键放开
}
if(k4==0) //有键按下吗?(k4=0 ?)
{ delay_ms(10); //延时消抖
if(k4==0)     //确实是有键按下,则:
{led4=!led4; //翻转灯的状态
    while(!k4);} //等待按键放开
}
if(k5==0) //有键按下吗?(k5=0 ?)
{ delay_ms(10); //延时消抖
if(k5==0)     //确实是有键按下,则:
{led5=!led5; //翻转灯的状态
    while(!k5);} //等待按键放开
}
if(k6==0) //有键按下吗?(k6=0 ?)
{ delay_ms(10); //延时消抖
if(k6==0)     //确实是有键按下,则:
{led6=!led6; //翻转灯的状态
    while(!k6);} //等待按键放开
}
if(k7==0) //有键按下吗?(k7=0 ?)
{ delay_ms(10); //延时消抖
if(k7==0)     //确实是有键按下,则:
{led7=!led7; //翻转灯的状态
    while(!k7);} //等待按键放开

C51单片机入门知识

阅读数 2135

没有更多推荐了,返回首页