历史上的今天
返回首页

历史上的今天

今天是:2024年11月08日(星期五)

正在发生

2021年11月08日 | 51单片机实现通过串口用计数延时方式发送一串数据

2021-11-08 来源:eefocus

一、使用proteus绘制简单的电路图,用于后续仿真

二、编写程序


/********************************************************************************************************************

---- @Project: USART

---- @File: main.c

---- @Edit: ZHQ

---- @Version: V1.0

---- @CreationTime: 20200714

---- @ModifiedTime: 20200714

---- @Description:

---- 波特率是:9600 。

---- 按一次按键S1,发送EB 00 55 01 00 00 00 00 41

---- 按一次按键S5,发送EB 00 55 02 00 00 00 00 42

---- 按一次按键S9,发送EB 00 55 03 00 00 00 00 43

---- 按一次按键S13,发送EB 00 55 04 00 00 00 00 44

---- 单片机:AT89C52

********************************************************************************************************************/

#include "reg52.h"

/*——————宏定义——————*/

#define FOSC 11059200L

#define BAUD 9600

#define T1MS (65536-FOSC/12/500)   /*0.5ms timer calculation method in 12Tmode*/

#define const_send_time 100 /*累计主循环次数的计数延时 根据项目实际情况来调整此数据大小*/

#define const_send_size 20 /*串口发送数据的缓冲区数组大小*/

#define const_Message_size 10 /*环形消息队列的缓冲区数组大小*/

#define const_key_time1 20 /*按键去抖动延时的时间*/

#define const_key_time2 20 /*按键去抖动延时的时间*/

#define const_key_time3 20 /*按键去抖动延时的时间*/

#define const_key_time4 20 /*按键去抖动延时的时间*/

 

#define const_voice_short 40 /*蜂鸣器短叫的持续时间*/

 

/*——————变量函数定义及声明——————*/

/*蜂鸣器的驱动IO口*/

sbit BEEP = P2^7;

/*LED*/

sbit LED = P3^5;

 

/*按键*/

sbit Key_S1 = P0^0; /*对应S1键*/

sbit Key_S2 = P0^1; /*对应S5键*/

sbit Key_S3 = P0^2; /*对应S9键*/

sbit Key_S4 = P0^3; /*对应S13键*/

sbit Key_Gnd = P0^4;

 

unsigned char ucSendregBuf[const_send_size]; /*接收串口中断数据的缓冲区数组*/

unsigned char ucMessageBuf[const_Message_size]; /*环形消息队列的缓冲区数据*/

 

unsigned int uiMessageCurrent = 0; /*环形消息队列的取数据当前位置*/

unsigned int uiMessageInsert = 0; /*环形消息队列的插入新消息时候的位置*/

unsigned int uiMessageCnt = 0; /*统计环形消息队列的消息数量  等于0时表示消息队列里没有消息*/

unsigned char ucMessage = 0; /*当前获取到的消息*/

 

/*为串口计时器多增加一个原子锁,作为中断与主函数共享数据的保护*/

unsigned char ucVoiceLock = 0; /*蜂鸣器鸣叫的原子锁*/

 

unsigned int uiVoiceCnt = 0; /*蜂鸣器鸣叫的持续时间计数器*/

 

unsigned char ucKeySec = 0; /*被触发的按键编号*/

unsigned int uiKeyTimeCnt1 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock1 = 0; /*按键触发后自锁的变量标志*/

unsigned int uiKeyTimeCnt2 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock2 = 0; /*按键触发后自锁的变量标志*/

unsigned int uiKeyTimeCnt3 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock3 = 0; /*按键触发后自锁的变量标志*/

unsigned int uiKeyTimeCnt4 = 0; /*按键去抖动延时计数器*/

unsigned char ucKeyLock4 = 0; /*按键触发后自锁的变量标志*/

 

unsigned char ucSendStep = 0; /*发送一串数据的运行步骤*/

unsigned int uiSendTimeCnt = 0; /*累计主循环次数的计数延时器*/

unsigned int uiSendCnt = 0; /*发送数据时的中间变量*/

/**

* @brief  定时器0初始化函数

* @param  无

* @retval 初始化T0

**/

void Init_T0(void)

{

TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/

TL0 = T1MS;                     /*initial timer0 low byte*/

TH0 = T1MS >> 8;                /*initial timer0 high byte*/

}

 

/**

* @brief  串口初始化函数

* @param  无

* @retval 初始化T0

**/

void Init_USART(void)

{

SCON = 0x50;

TMOD = 0x21;                    

TH1=TL1=-(FOSC/12/32/BAUD);

}

 

/**

* @brief  外围初始化函数

* @param  无

* @retval 初始化外围

* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。

* 只要更改以下对应变量的内容,就可以显示你想显示的数字。

**/

void Init_Peripheral(void)

{

ET0 = 1;/*允许定时中断*/

TR0 = 1;/*启动定时中断*/

TR1 = 1;

ES = 1; /*允许串口中断*/

EA = 1;/*开总中断*/  

}

 

/**

* @brief  初始化函数

* @param  无

* @retval 初始化单片机

**/

void Init(void)

{

LED  = 0;

BEEP = 1;

Key_Gnd = 0;

Init_T0();

Init_USART();

}

/**

* @brief  延时函数

* @param  无

* @retval 无

**/

void Delay_Long(unsigned int uiDelayLong)

{

   unsigned int i;

   unsigned int j;

   for(i=0;i   {

      for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/

          {

             ; /*一个分号相当于执行一条空语句*/

          }

   }

}

// /**

// * @brief  延时函数

// * @param  无

// * @retval 无

// **/

// void Delay_Short(unsigned int uiDelayShort)

// {

//   unsigned int i;

//   for(i=0;i//   {

// ; /*一个分号相当于执行一条空语句*/

//   }

// }

/**

* @brief  插入信息函数

* @param  无

* @retval 插入新的消息到环形消息队列里

* 通过判断数组下标是否超范围的条件,把一个数组的首尾连接起来,就像一个环形,

* 因此命名为环形消息队列。环形消息队列有插入消息,获取消息两个核心函数,以及一个

* 统计消息总数的uiMessageCnt核心变量,通过此变量,我们可以知道消息队列里面是否有消息需要处理.

* 做项目中很少用消息队列的,大部分的单片机项目其实直接用一两个中间变量就可以起到传递消息的作用,就能满足系统的要求。

* 以下是各变量的含义: 

* #define const_Message_size  10  //环形消息队列的缓冲区数组大小

* unsigned char ucMessageBuf[const_Message_size]; //环形消息队列的缓冲区数据

* unsigned int  uiMessageCurrent=0;  //环形消息队列的取数据当前位置

* unsigned int  uiMessageInsert=0;  //环形消息队列的插入新消息时候的位置

* unsigned int  uiMessageCnt=0;  //统计环形消息队列的消息数量  等于0时表示消息队列里没有消息

**/

void insert_message(unsigned char ucMessageTemp) /*插入新的消息到环形消息队列里*/

{

if(uiMessageCnt < const_Message_size) /*消息总数小于环形消息队列的缓冲区才允许插入新消息*/

{

ucMessageBuf[uiMessageInsert] = ucMessageTemp;

uiMessageInsert ++; /*插入新消息时候的位置*/

if(uiMessageInsert >= const_Message_size) /*到了缓冲区末尾,则从缓冲区的开头重新开始。数组的首尾连接,看起来就像环形*/

{

uiMessageInsert = 0;

}

uiMessageCnt ++; /*消息数量累加  等于0时表示消息队列里没有消息*/

}

}

 

/**

* @brief  提取信息函数

* @param  无

* @retval 从环形消息队列里提取消息

**/

unsigned char get_message(void) /*从环形消息队列里提取消息*/

{

unsigned char ucMessageTemp = 0; /*返回的消息中间变量,默认为0*/

if(uiMessageCnt > 0) /*只有消息数量大于0时才可以提取消息*/

{

ucMessageTemp = ucMessageBuf[uiMessageCurrent];

uiMessageCurrent ++; /*环形消息队列的取数据当前位置*/

if(uiMessageCurrent >= const_Message_size) /*到了缓冲区末尾,则从缓冲区的开头重新开始。数组的首尾连接,看起来就像环形*/

{

uiMessageCurrent = 0;

}

uiMessageCnt --; /*每提取一次,消息数量就减一  等于0时表示消息队列里没有消息*/

}

return ucMessageTemp;

}

 

/**

* @brief  串口发送函数

* @param  ucSendData

* @retval 在发送一串数据中,每个字节之间必须添加一个延时,用来等待串口发送完成。

* 不增加延时,单单靠发送完成标志位来判断还是容易出错,在51,PIC单片机中都是这么做。

* 在stm32单片机中,可以不增加延时,直接靠单片机自带的标志位来判断就很可靠。

**/

void eusart_send(unsigned char ucSendData)

{

ES = 0; /*关串口中断*/

TI = 0; /*清零串口发送完成中断请求标志*/

SBUF = ucSendData; /*发送一个字节*/

 

// Delay_Short(400); /*因为外部在每个发送字节之间用了累计主循环次数的计数延时,因此不要此行的delay延时*/

 

TI = 0; /*清零串口发送完成中断请求标志*/

ES = 1; /*允许串口中断*/

}

 

/**

* @brief  发送服务函数

* @param  无

* @retval 利用累计主循环次数的计数延时方式来发送一串数据

**/

void send_service(void) /*利用累计主循环次数的计数延时方式来发送一串数据*/

{

switch(ucSendStep) /*发送一串数据的运行步骤*/

{

case 0: /*从环形消息队列里提取消息*/

if(uiMessageCnt > 0) /*说明有消息需要处理*/

{

ucMessage = get_message();

switch(ucMessage) /*消息处理*/

{

case 1:

ucSendregBuf[0] = 0xeb; /*把准备发送的数据放入发送缓冲区*/

ucSendregBuf[1] = 0x00;

ucSendregBuf[2] = 0x55;

ucSendregBuf[3] = 0x01; /*01代表1号键*/

ucSendregBuf[4] = 0x00;

ucSendregBuf[5] = 0x00;

ucSendregBuf[6] = 0x00;

ucSendregBuf[7] = 0x00;

ucSendregBuf[8] = 0x41;

 

uiSendCnt = 0; /*发送数据的中间变量清零*/

uiSendTimeCnt = 0; /*累计主循环次数的计数延时器清零*/

ucSendStep =1; /*切换到下一步发送一串数据*/

break;

case 2:

ucSendregBuf[0] = 0xeb; /*把准备发送的数据放入发送缓冲区*/

ucSendregBuf[1] = 0x00;

ucSendregBuf[2] = 0x55;

ucSendregBuf[3] = 0x02; /*02代表2号键*/

ucSendregBuf[4] = 0x00;

ucSendregBuf[5] = 0x00;

ucSendregBuf[6] = 0x00;

ucSendregBuf[7] = 0x00;

ucSendregBuf[8] = 0x42;

 

uiSendCnt = 0; /*发送数据的中间变量清零*/

uiSendTimeCnt = 0; /*累计主循环次数的计数延时器清零*/

ucSendStep =1; /*切换到下一步发送一串数据*/

break;

case 3:

ucSendregBuf[0] = 0xeb; /*把准备发送的数据放入发送缓冲区*/

ucSendregBuf[1] = 0x00;

ucSendregBuf[2] = 0x55;

ucSendregBuf[3] = 0x03; /*03代表3号键*/

ucSendregBuf[4] = 0x00;

ucSendregBuf[5] = 0x00;

ucSendregBuf[6] = 0x00;

ucSendregBuf[7] = 0x00;

ucSendregBuf[8] = 0x43;

 

uiSendCnt = 0; /*发送数据的中间变量清零*/

uiSendTimeCnt = 0; /*累计主循环次数的计数延时器清零*/

ucSendStep =1; /*切换到下一步发送一串数据*/

break;

case 4:

ucSendregBuf[0] = 0xeb; /*把准备发送的数据放入发送缓冲区*/

ucSendregBuf[1] = 0x00;

ucSendregBuf[2] = 0x55;

ucSendregBuf[3] = 0x04; /*04代表4号键*/

ucSendregBuf[4] = 0x00;

ucSendregBuf[5] = 0x00;

ucSendregBuf[6] = 0x00;

ucSendregBuf[7] = 0x00;

ucSendregBuf[8] = 0x44;

 

uiSendCnt = 0; /*发送数据的中间变量清零*/

uiSendTimeCnt = 0; /*累计主循环次数的计数延时器清零*/

ucSendStep =1; /*切换到下一步发送一串数据*/

break;

default: /*如果没有符合要求的消息,则不处理*/

ucSendStep = 0; /*维持现状,不切换*/

break;

}

}

break;

case 1: /*利用累加主循环次数的计数延时方式来发送一串数据*/

/* 

* 这里的计数延时为什么不用累计定时中断次数的延时,而用累计主循环次数的计数延时?

* 因为本程序定时器中断一次需要500个指令时间,时间分辨率太低,不方便微调时间。因此

* 就用累计主循环次数的计数延时方式,在做项目的时候,应该根据系统的实际情况

* 来调整const_send_time的大小。

*/ 

uiSendTimeCnt ++; /*累计主循环次数的计数延时,为每个字节之间增加延时*/

if(uiSendTimeCnt > const_send_time) /*请根据实际系统的情况,调整const_send_time的大小*/

{

uiSendTimeCnt = 0;

eusart_send(ucSendregBuf[uiSendCnt]); /*发送一串数据给上位机*/

uiSendCnt ++;

if(uiSendCnt >= 9) /*说明数据已经发送完毕*/

{

uiSendCnt = 0;

ucSendStep = 0; /*返回到上一步,处理其它未处理的消息*/

}

}

break;

}

}

 

/**

* @brief  按键扫描函数

* @param  无

* @retval 放在定时中断里

**/

void key_scan(void)

{

if(Key_S1 == 1) /*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/

{

ucKeyLock1 = 0; /*按键自锁标志清零*/

uiKeyTimeCnt1 = 0; /*按键去抖动延时计数器清零*/

}

else if(ucKeyLock1 == 0) /*有按键按下,且是第一次被按下*/

{

uiKeyTimeCnt1 ++; /*累加定时中断次数*/

if(uiKeyTimeCnt1 > const_key_time1)

{

uiKeyTimeCnt1 = 0;

ucKeyLock1 = 1; /*自锁按键置位,避免一直触发*/

ucKeySec = 1;

}

}

if(Key_S2 == 1) /*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/

{

ucKeyLock2 = 0; /*按键自锁标志清零*/

uiKeyTimeCnt2 = 0; /*按键去抖动延时计数器清零*/

}

else if(ucKeyLock2 == 0) /*有按键按下,且是第一次被按下*/

{

uiKeyTimeCnt2 ++; /*累加定时中断次数*/

if(uiKeyTimeCnt2 > const_key_time2)

{

uiKeyTimeCnt2 = 0;

ucKeyLock2 = 1; /*自锁按键置位,避免一直触发*/

ucKeySec = 2;

}

}

if(Key_S3 == 1) /*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/

{

ucKeyLock3 = 0; /*按键自锁标志清零*/

uiKeyTimeCnt3 = 0; /*按键去抖动延时计数器清零*/

}

else if(ucKeyLock3 == 0) /*有按键按下,且是第一次被按下*/

{

uiKeyTimeCnt3 ++; /*累加定时中断次数*/

if(uiKeyTimeCnt3 > const_key_time3)

{

uiKeyTimeCnt3 = 0;

ucKeyLock3 = 1; /*自锁按键置位,避免一直触发*/

ucKeySec = 3;

}

}

if(Key_S4 == 1) /*IO是高电平,说明按键没有被按下,这时要及时清零一些标志位*/

{

ucKeyLock4 = 0; /*按键自锁标志清零*/

uiKeyTimeCnt4 = 0; /*按键去抖动延时计数器清零*/

}

else if(ucKeyLock4 == 0) /*有按键按下,且是第一次被按下*/

推荐阅读

史海拾趣

CTS公司的发展小趣事

面对不断变化的市场需求和行业竞争,CTS公司始终保持创新精神。公司加大了在研发领域的投入,致力于开发具有更高性能、更可靠性的产品。同时,CTS还关注新兴技术的发展趋势,积极探索新的应用领域和市场机会。

这五个故事展示了CTS公司在电子行业中的发展历程和取得的成就。从创立初期的艰难起步到技术突破、业务扩展、收购整合、全球化布局以及持续创新,CTS始终保持着积极进取的精神和不断创新的态度,为电子行业的发展做出了重要贡献。

DCCOM [ DC COMPONENTS ]公司的发展小趣事

DCCOM公司自创立之初,就注重技术创新和研发投入。在成立初期,公司研发团队成功开发出一种具有高性能和低功耗特点的电子元件,这一创新技术迅速赢得了市场的认可。随着技术的不断迭代和升级,DCCOM逐渐在电子元件市场上占据了一席之地。

爱普特半导体(APTSEMI)公司的发展小趣事

爱普特半导体的产品因其卓越的性能和稳定的质量,赢得了众多大型企业的青睐。公司与小米、美的、阿里、LG等多家知名企业建立了战略合作关系。这些合作不仅为爱普特带来了稳定的订单和市场份额,还提升了公司的品牌影响力和行业地位。通过与这些企业的深度合作,爱普特不断吸收行业最新技术和管理经验,推动了自身技术水平和市场竞争力的提升。

深圳杜因特(DOINGTER)公司的发展小趣事

作为一家快速发展的电子企业,杜因特深知人才是公司最宝贵的财富。因此,公司始终将团队建设和人才培养放在重要位置。通过引进优秀人才、加强内部培训等方式,杜因特打造了一支高效协作、专业精湛的团队。同时,公司还为员工提供了良好的工作环境和福利待遇,让员工能够在一个如家般的团队中做具有革新性的工作。

以上五个故事仅是基于已知信息对深圳杜因特公司发展起来的相关事实的概括性描述,具体细节可能因实际情况而有所不同。

DFI公司的发展小趣事

在稳固了国内市场地位后,DFI开始将目光投向全球市场。公司积极开拓北美、拉丁美洲和亚太地区的业务,通过参加国际展会、与当地企业建立合作关系等方式,不断提升品牌知名度和市场份额。同时,DFI还针对不同地区的市场需求,推出定制化的产品和服务,以满足客户的多样化需求。这一全球化战略使DFI在国际市场上取得了显著的成绩。

谷峰(GOFORD)公司的发展小趣事

随着产品线的不断丰富和技术实力的日益增强,GStek开始积极拓展国内外市场。公司采取多元化的市场策略,针对不同客户群体提供定制化的解决方案。同时,GStek还注重品牌建设,通过参加各类行业展会、举办技术研讨会等方式,加强与业界的交流与合作,提升品牌知名度和影响力。这些努力使得GStek的产品广泛应用于各类电子产品中,包括智能手机、平板电脑、笔记本电脑等移动设备以及家电、工控等领域。

问答坊 | AI 解惑

3000语音信号处理

3000语音信号处理…

查看全部问答>

大家使用lpc2132注意的一点

关于__irq 的使用 __irq为一个标识,用来表示一个函数是否为中断函数。对于不同的编译器,__irq在函数名中的位置不一样,例如: ADS编译器中 : void __irq IRQ_Eint0(void); Keil编译器中 : void IRQ_Eint0(void) __irq; 但是其意义一 ...…

查看全部问答>

ce软键盘问题

我用evc4.0写了一个软键盘程序,是基于对话框的,单击按钮模拟按键消息keybd_event,在模拟器上调试一切正常,可拿到2440板子上一试问题就出现了,我的程序A需要输入,可点击软键盘,模拟字符输出a-z,正常应该是两个keydown和keyup,可程序A只接收 ...…

查看全部问答>

关于51学习板USB不能控制供电 和 不停执行

我买了块AT89S52学习板 有2个接口 一根是USB借口 这个接了之后 电源按钮可以控制单片机 但是还有一个是ISP并行口转USB的接口 接了之后就不能控制电源了 就一直供电 很郁闷 而且还有1个问题就是用KEIL写了段最简单的程序 为什么我没有用循环语句 也 ...…

查看全部问答>

请问NAND FLAHS/NOR FLASH,PSRAM方面的文档

请问NAND FLAHS/NOR FLASH,PSRAM方面的文档或者书籍,哪位接触过?工作中遇到一些存储方案的问题,看到NOR FLASH + PSRAM、NAND FLASH + LPSDRAM等方案,只了解少许inteface/features上的差别,我希望能看到些在memory 架构、电路、工艺级的信息 ...…

查看全部问答>

关于DSP+FPGA系统 时钟问题

    我做的一个基于DSP的系统中,DSP做主处理器,控制着整个系统,包括信号处理,整体调度等;选择了一块Xilinx的FPGA做FIFO UART和系统的逻辑控制和译码。DSP的时钟输入为15MHz,经过内部的PLL倍频为较高频率,FPGA需要25M或一下的时钟输 ...…

查看全部问答>

我见过讲Makefile最好中文文章

建议大家看一下,做些很厉害,讲的很到位很全面!~…

查看全部问答>

LM3S811 + VPC3+S 震撼Profibus DP方案

利用M3的高速SPI或则I2C来控制VPC3+S的Profibus-DP方案,非常受到广大客户青睐。仅占用MCU的4到8个管脚就可以做成一个Profibus DP网关。而且本身LM3S811体积非常小,VPC3+S更是BGA48的超小体积,所以非常棒。   有需要做得可以找我,提供免 ...…

查看全部问答>

单片机I/O控制电源开断

我现在想用单片机的I/O口来控制30路电源的开断(电源都是12V,1A),因为有很多路要控制,所以不想用继电器,太占板子的空间了。请问下大家有没有什么好的电路或者是方法可以推荐一下。每一路电源都是相互独立的,考虑到I/O的数量,如果能节省点I/O ...…

查看全部问答>

对于具有过流保护,自恢复功能的led电路的问题?

有8个LED灯,限流是370mA。我是把电流一下降到只有1个LED都不会过流的电压好,还是慢慢的把电压降下来好????????????…

查看全部问答>