找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1916|回复: 17
打印 上一主题 下一主题
收起左侧

51单片机Uart和定时器0同时使用问题

[复制链接]
回帖奖励 200 黑币 回复本帖可获得 2 黑币奖励! 每人限 1 次
跳转到指定楼层
楼主
ID:1071654 发表于 2023-8-19 15:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
如下为STC89C52单片机制作智能小车,蓝牙遥控,红外循迹均可单独使用,两个功能整合在一起时候蓝牙遥控指令只有红外循迹可使用了,其他指令小车不执行对应动作,请问代码是哪里有问题,望指出,感谢!
1、怀疑蓝牙发1-6时候,电机驱动并未使能,所以相应动作未执行,不太确认是否是Timer0定时器未工作原因?
2、我用Timer0做电机驱动使能控制,Timer1做Uart,可T0的中断优先级比T1的要高,如何让两个同时用而不冲突呢?
3、另外,是否能在主函数给电机驱动定义变量,其他子函数,包括中断都能调用呢?如下代码有在主函数定义变量,同时在红外循迹中也定义了相同变量,如不定义,无法编译
以上问题,希望各位大神指点下思路。

主函数

#include <REGX52.H>
#include "UART.H"
#include "MotorDrive.H"
#include "Timer0.H"
#include "IR_Tracking.H"

sbit L298N_ENA=P1^4;
sbit L298N_ENB=P1^5;
sbit L298N_ENA1=P1^1;
sbit L298N_ENB1=P0^2;

extern unsigned char Compare,Compare1,Compare2,Compare3;
extern unsigned char Counter,Counter1,Counter2,Counter3;



void main()
{
        UART_Init();
        Timer0_Init();
//        L298N_ENA=1;
//        L298N_ENB=1;
//        L298N_ENA1=1;
//        L298N_ENB1=1;
        while(1)
        {
               
        }
}


void Timer0_Routine() interrupt 1
{
        TL0=0xA4;  //设置定时初值
        TH0=0xFF;  //设置定时初值
        Counter++;
        Counter%=100;                //计数值变化范围限制在0~99
        Counter1++;
        Counter1%=100;        //计数值变化范围限制在0~99
        Counter2++;
        Counter2%=100;        //计数值变化范围限制在0~99
        Counter3++;
        Counter3%=100;        //计数值变化范围限制在0~99
        if(Counter<Compare)
        {
                L298N_ENA=1;
        }
        else
        {
                L298N_ENA=0;
        }
        if(Counter1<Compare1)
        {
                L298N_ENB=1;
        }
        else
        {
                L298N_ENB=0;
        }
        if(Counter2<Compare2)
        {
                L298N_ENA1=1;
        }
        else
        {
                L298N_ENA1=0;
        }
        if(Counter3<Compare3)
        {
                L298N_ENB1=1;
        }
        else
        {
                L298N_ENB1=0;
        }
}

void UART_Routine() interrupt 4    //串口中断
{         
unsigned char Data;
if(RI==1)   //允许单片机接收数据
{
        Data=SBUF;                   //读取数据
        UART_SendByte(SBUF);  //将接收到的数据发回串口
        RI=0;  //接收标志位清0 软件复位
switch(Data)
  {
        case 0:StopIt();                break;
        case 1:Forward();                break;
        case 2:BackOff();                break;
        case 3:TurnLeft();        break;
        case 4:TurnRight();        break;
        case 5:TurnLeftCircle();                break;
        case 6:TurnRightCircle();                break;
        case 7:IR_Tracking(); break;

  }
}
}



UART

#include <REGX52.H>

/**
*@brief  串口初始化9600bps@11.0592MHz
*@param  无
*retval  无
**/
void UART_Init()                //9600bps@11.0592MHz
{
        PCON &= 0x7F;                //波特率不倍速
        SCON = 0x50;                //8位数据,可变波特率
        TMOD &= 0x0F;                //清除定时器1模式位
        TMOD |= 0x20;                //设定定时器1为8位自动重装方式
        TL1 = 0xFD;                //设定定时初值
        TH1 = 0xFD;                //设定定时器重装值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;        //启动定时器1
        EA        =1;
        ES        =1;
}

/**
*@brief  串口发送一个数据
*@param  Byte
*retval  无
*/

void UART_SendByte(unsigned char Byte)
{
        SBUF=Byte;
        while(TI==0);
        TI=0;
}

电机驱动

#include <REGX52.H>

sfr P4=0xe8;

sbit L298N_IN1        =P1^2;
sbit L298N_IN2        =P1^3;        //IN1&1N2是右前轮
sbit L298N_IN3        =P1^6;
sbit L298N_IN4        =P1^7;        //IN3&IN4是右后轮

sbit L298N_IN41        =P0^1;
sbit L298N_IN31        =P0^0;        //IN31&IN41是左前轮
sbit L298N_IN21        =P4^2;
sbit L298N_IN11        =P1^0;        //IN21&IN11是左后轮

void Forward()  //前进
{
                L298N_IN1=0;
                L298N_IN2=1;
                L298N_IN3=0;
                L298N_IN4=1;
                L298N_IN11=1;
                L298N_IN21=0;
                L298N_IN31=1;
                L298N_IN41=0;
}

void BackOff()        //后退
{

                L298N_IN1=1;
                L298N_IN2=0;
                L298N_IN3=1;
                L298N_IN4=0;
                L298N_IN11=0;
                L298N_IN21=1;
                L298N_IN31=0;
                L298N_IN41=1;
}

void TurnRight()        //右转
{
                L298N_IN11=1;
                L298N_IN21=0;
                L298N_IN31=1;
                L298N_IN41=0;
}

void TurnRightCircle()        //右转圈
{
                L298N_IN1=1;
                L298N_IN2=0;
                L298N_IN3=1;
                L298N_IN4=0;
                L298N_IN11=1;
                L298N_IN21=0;
                L298N_IN31=1;
                L298N_IN41=0;
}

void TurnLeft()        //左转
{
                L298N_IN1=0;
                L298N_IN2=1;
                L298N_IN3=0;
                L298N_IN4=1;
        
}

void TurnLeftCircle()        //左转圈
{
                L298N_IN1=0;
                L298N_IN2=1;
                L298N_IN3=0;
                L298N_IN4=1;
                L298N_IN11=0;
                L298N_IN21=1;
                L298N_IN31=0;
                L298N_IN41=1;
}

void StopIt()        //停止
{
                L298N_IN1=0;
                L298N_IN2=0;
                L298N_IN3=0;
                L298N_IN4=0;
                L298N_IN11=0;
                L298N_IN21=0;
                L298N_IN31=0;
                L298N_IN41=0;
}


Timer0

#include <REGX52.H>

void Timer0_Init()        //100微秒@11.0592MHz
{
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0xA4;                //设置定时初值
        TH0 = 0xFF;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
        ET0=1;
        EA=1;
        PT0=1;
}




红外循迹

#include <REGX52.H>
#include "MotorDrive.H"

sbit LM339_OUT1=P0^4;        //IR_1
sbit LM339_OUT2=P0^5;        //IR_2
sbit LM339_OUT3=P0^7;        //IR_3
sbit LM339_OUT4=P0^6;        //IR_4

unsigned char Compare,Compare1,Compare2,Compare3;
unsigned char Counter,Counter1,Counter2,Counter3;

void IR_Tracking()
{
        while(1)
        {
                if(LM339_OUT2==0&&LM339_OUT3==0)
{        
                        Compare=8;
                        Compare1=8;
                        Compare2=8;
                        Compare3=8;
                        Forward();
}

                if(LM339_OUT2==1&&LM339_OUT3==1)
{
                        StopIt();
}
               
                if(LM339_OUT2==1&&LM339_OUT3==0)
{
                        Compare=50;
                        Compare1=50;
                        TurnLeftCircle();
}
                if(LM339_OUT2==0&&LM339_OUT3==1)
{
                        Compare2=50;
                        Compare3=50;
                        TurnRightCircle();
}

        }

                }






分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

来自 2#
ID:123289 发表于 2023-8-21 09:27 | 只看该作者
2、我用Timer0做电机驱动使能控制,Timer1做Uart,可T0的中断优先级比T1的要高,如何让两个同时用而不冲突呢?
说明你很聪明,发现问题了。
解决的方案:
让T0的中断服务程序运行时间,低于串行CLK的半个周期,如波特率=2400,周期=417us。
如何办到:
在【T0的中断服务程序】中,只做一个标记【ZDT0=1】,表示发生了中断。其它需要处理的事项,放在主程序去做。主程序识别到【ZDT0=1】,就处理中断事件,处理完成后复柆【ZDT0=0】。
这样就不会影响T1中断了。
明白了这个道理之后,记住【这是精华】:所有的【中断服务程序】都要尽可能的短。方法:【中断服务程序】只做标记,不做中断事件处理(除非万不得矣),中断事件的处理,尽可能放在主程序中去做!

评分

参与人数 1黑币 +5 收起 理由
wen1989jun + 5

查看全部评分

回复

使用道具 举报

来自 3#
ID:1073939 发表于 2023-8-24 12:01 | 只看该作者
wen1989jun 发表于 2023-8-23 18:04
经过4楼大神的指导,现在把红外循迹代码放到主程序来了,把Uart里面中断处理事情放在主程序来了之后,实现 ...
当红外循迹指令发给MCU后,其他指令MCU能识别及返回,但不执行相应的动作了

只需要把IR_Tracking()函数中的“while(1)”这行注释就可以了。

你代码还有些问题。
Data这个全局变量表示最后收到的数据,还需要增加一个全局变量表示这数据是否已经处理。
  1. bit gbTrack=0;
  2. bit gbRI=0;
  3. void main()
  4. {
  5.         UART_Init();
  6.         Timer0_Init();
  7.         Compare = Compare1 = Compare2 = Compare3 = 20;
  8.         while(1)
  9.         {
  10.                 if(gbTrack)
  11.                         IR_Tracking();//循迹
  12.                 if(gbRI)//有新的串口数据 在串口接收中断中加入gbRI=1;
  13.                 {
  14.                         gbRI=0;//新的串口数据已处理
  15.                         gbTrack=0;//除循迹指令外,其它指令不循迹
  16.                         if(Data == 7)
  17.                                 gbTrack=1;//收到循迹指令
  18.                         else if(Data == 0)
  19.                                 StopIt();
  20.                         else if(Data == 1)
  21.                         {
  22.                                 Compare = Compare1 = Compare2 = Compare3 = 20;
  23.                                 Forward();
  24.                         }
  25.                         else if(Data == 2)
  26.                         {
  27.                                 Compare = Compare1 = Compare2 = Compare3 = 20;
  28.                                 BackOff();
  29.                         }
  30.                 }
  31.         }
  32. }
复制代码


回复

使用道具 举报

地板
ID:1071654 发表于 2023-8-19 15:54 | 只看该作者
自己先顶下帖子
回复

使用道具 举报

5#
ID:1071654 发表于 2023-8-21 09:23 | 只看该作者
自顶下,求大神解惑
回复

使用道具 举报

6#
ID:277550 发表于 2023-8-21 10:16 | 只看该作者
主要原因是在中斷中,語句耗時太多。

~
回复

使用道具 举报

7#
ID:1071654 发表于 2023-8-21 15:16 | 只看该作者
devcang 发表于 2023-8-21 10:16
主要原因是在中斷中,語句耗時太多。

~

能展开详细说说么,我来验证下
回复

使用道具 举报

8#
ID:1071654 发表于 2023-8-21 15:51 | 只看该作者
yzwzfyz 发表于 2023-8-21 09:27
2、我用Timer0做电机驱动使能控制,Timer1做Uart,可T0的中断优先级比T1的要高,如何让两个同时用而不冲突 ...

Uart中断做标志位,主程序做识别,之前中断里面的Switch可以放在主程序里面去做即可对么?

其它需要处理的事项,放在主程序去做。主程序识别到【ZDT0=1】,就处理中断事件,处理完成后复柆【ZDT0=0】。
通过这句话我又感觉不能用Switch去做了

真是抱歉,C语言和单片机都是初学,还有很多不理解的地方,一会儿我去试试写代码验证看看,还是非常感谢您的解答!!
回复

使用道具 举报

9#
ID:1071654 发表于 2023-8-23 18:04 | 只看该作者
经过4楼大神的指导,现在把红外循迹代码放到主程序来了,把Uart里面中断处理事情放在主程序来了之后,实现了蓝牙遥控小车及红外循迹功能了。
出现了一个新的问题,当红外循迹指令发给MCU后,其他指令MCU能识别及返回,但不执行相应的动作了

#include <REGX52.H>
#include <Timer0.H>
#include "UART.H"
#include "MotorDrive.H"

sbit L298N_ENA=P1^4;
sbit L298N_ENB=P1^5;
sbit L298N_ENA1=P1^1;
sbit L298N_ENB1=P0^2;

sbit LM339_OUT1=P0^4;        //IR_1
sbit LM339_OUT2=P0^5;        //IR_2
sbit LM339_OUT3=P0^7;        //IR_3
sbit LM339_OUT4=P0^6;        //IR_4

unsigned char Compare,Compare1,Compare2,Compare3;
unsigned char Counter,Counter1,Counter2,Counter3;

unsigned char Data;

void IR_Tracking()
{
        while(1)
        {
                if(LM339_OUT2==0&&LM339_OUT3==0)
{       
                        Compare=8;
                        Compare1=8;
                        Compare2=8;
                        Compare3=8;
                        Forward();
}

                if(LM339_OUT2==1&&LM339_OUT3==1)
{
                        StopIt();
}
               
                if(LM339_OUT2==1&&LM339_OUT3==0)
{
                        Compare=50;
                        Compare1=50;
                        TurnLeftCircle();
}
                if(LM339_OUT2==0&&LM339_OUT3==1)
{
                        Compare2=50;
                        Compare3=50;
                        TurnRightCircle();
}

        }

                }

void main()
{
        UART_Init();
        Timer0_Init();
        Compare=20;
        Compare1=20;
        Compare2=20;
        Compare3=20;
        while(1)
        {
                if(Data==1)
                {
                        Compare=20;
                        Compare1=20;
                        Compare2=20;
                        Compare3=20;
                        Forward();
                }
                if(Data==2)
                {
                        Compare=20;
                        Compare1=20;
                        Compare2=20;
                        Compare3=20;
                        BackOff();
                }
                if(Data==7)
                {
                        IR_Tracking();
                }
                if(Data==0)
                {
                        StopIt();
                }
        }
}


void Timer0_Routine() interrupt 1
{
        TL0=0xA4;  //设置定时初值
        TH0=0xFF;  //设置定时初值
        Counter++;
        Counter%=100;                //计数值变化范围限制在0~99
        Counter1++;
        Counter1%=100;        //计数值变化范围限制在0~99
        Counter2++;
        Counter2%=100;        //计数值变化范围限制在0~99
        Counter3++;
        Counter3%=100;        //计数值变化范围限制在0~99
        if(Counter<Compare)
        {
                L298N_ENA=1;
        }
        else
        {
                L298N_ENA=0;
        }
        if(Counter1<Compare1)
        {
                L298N_ENB=1;
        }
        else
        {
                L298N_ENB=0;
        }
        if(Counter2<Compare2)
        {
                L298N_ENA1=1;
        }
        else
        {
                L298N_ENA1=0;
        }
        if(Counter3<Compare3)
        {
                L298N_ENB1=1;
        }
        else
        {
                L298N_ENB1=0;
        }
}

void UART_Routine() interrupt 4    //串口中断
{         

if(RI==1)   //允许单片机接收数据
{
        Data=SBUF;                   //读取数据
        UART_SendByte(SBUF);  //将接收到的数据发回串口
        RI=0;  //接收标志位清0 软件复位
}
}
回复

使用道具 举报

10#
ID:844772 发表于 2023-8-24 10:43 | 只看该作者

UART_SendByte(SBUF);  //将接收到的数据发回串口
的后边加一句:
Data-=48;
回复

使用道具 举报

11#
ID:1071654 发表于 2023-8-24 16:53 | 只看该作者
glinfei 发表于 2023-8-24 10:43

UART_SendByte(SBUF);  //将接收到的数据发回串口
的后边加一句:

按照您的指点在:UART_SendByte(SBUF); 后面增加了Data-=48;
验证失败,串口能收到数据,但所有指令均不识别,小车无任何动作
回复

使用道具 举报

12#
ID:1071654 发表于 2023-8-24 16:57 | 只看该作者
ydatou 发表于 2023-8-24 12:01
只需要把IR_Tracking()函数中的“while(1)”这行注释就可以了。

你代码还有些问题。

把IR_Tracking()函数中的“while(1)”这行注释就可以了。
//如上信息经过验证,蓝牙控制和红外循迹指令均能识别。谢谢您

你代码还有些问题。
Data这个全局变量表示最后收到的数据,还需要增加一个全局变量表示这数据是否已经处理。
//这部分代码验证中,感谢您的指导
回复

使用道具 举报

13#
ID:1071654 发表于 2023-8-29 16:45 | 只看该作者
ydatou 发表于 2023-8-24 12:01
只需要把IR_Tracking()函数中的“while(1)”这行注释就可以了。

你代码还有些问题。

此部分代码已验证,可行。
回复

使用道具 举报

14#
ID:1071654 发表于 2023-8-29 16:47 | 只看该作者
有一个小小的疑问,在主程序中定义了:
unsigned char Compare,Compare1,Compare2,Compare3;
unsigned char Counter,Counter1,Counter2,Counter3;
如果要把IR_Tracking()模块化,那么是否也需要在IR_Tracking()里面再次定义?有点不太明白
回复

使用道具 举报

15#
ID:1073939 发表于 2023-8-30 08:36 | 只看该作者
要在多个文件使用同一变量做法:
只在一个文件正常定义变量,其它文件变量定义加“extern”修饰,“extern”表示该变量定义在其它文件中。

其它方法,只在一个文件正常定义变量,其它文件不直接操作该变量,依赖函数操作。

回复

使用道具 举报

16#
ID:1071654 发表于 2023-9-1 10:44 | 只看该作者
ydatou 发表于 2023-8-30 08:36
要在多个文件使用同一变量做法:
只在一个文件正常定义变量,其它文件变量定义加“extern”修饰,“extern ...

验证可行,感谢哦!
回复

使用道具 举报

17#
ID:517951 发表于 2023-9-5 07:48 | 只看该作者
建议把中断里的函数拿出来, 放到main()中来执行.
中断函数只需要做检测外部事件, 根据外部事件设标志位,
main()
{
  while(1)
  {
   if(标志位1) function1();
  }
}
中根据外部事件标志位来执行相应的执行功能.
回复

使用道具 举报

18#
ID:1071654 发表于 2023-9-5 09:28 | 只看该作者
rayin 发表于 2023-9-5 07:48
建议把中断里的函数拿出来, 放到main()中来执行.
中断函数只需要做检测外部事件, 根据外部事件设标志位,
...

谢谢您的指点,当前按在您的方式来做,验证功能可行
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表