找回密码
 立即注册

QQ登录

只需一步,快速开始

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

stc15W105单片机怎么最快将DI口数据赋给DO口?

[复制链接]
跳转到指定楼层
楼主
问题:DI口输入一组400*24bit的数据,如何使用单片机实现:前40*24bit的数据不接收,剩下的数据全部转给DO口输出,并保证输出数据的准确性,不乱码。输入数据0码高电平时间为300ns左右。


已尝试方法:
使用单片机型号为stc15W105


#include <IAP15F105W.h>/**
  * @Brief  IO口初始化
  * @param  null
  * @retval null
  */
sbit DI = P3^2;
sbit DO = P3^3;
void IO_Init(void)
{
        P3M0 |= 0x08;        //P33设置成推挽输出
        P3M1 &= 0xF7;
      
        P3M1 |= 0x04;        //P32设置成高阻输入
        P3M0 &= 0xFB;
        DO=0;


        //把P32脚设置为下降沿触发方式
        //P32是外部中断0输入脚
        IT0 = 1;        //外部中断0触发方式,=1下降沿触发,=0双边沿触发
        EX0 = 1;        //允许外部中断0中断
}
bit flag;
unsigned int count;//定时器计数值

void main(void)
{
      
        count=0;
        IO_Init();    //初始化
        EA = 1;       //开中断
        while(1)
        {
         if(flag)     //当过了20*24bit之后,输出等于输入
         {
                 DO = DI;
         }      
                ;
  }
}
      
      
void Int0_Routine(void)   interrupt 0  //外部中断0服务函数
        {
                if(DI ==1)
                {
                        DO =1;
                }
                else
                {
                        DO=0;
                }
                if(count<960)
                {
                count++;
                }else
                {
                        flag=1;
                }
                TR0=0;
        }
      

void Timer0_Routine(void)   interrupt 1  //定时器0中断服务函数
        {
                count = 0;
                flag=0;
        }  
     
发现处理速度过慢,导致输出数据0码高电平时间达到us级,全被判为1码
尝试过不进行计数,直接将DO=DI,使DI口所有数据全部转发,发现一部分0码数据的高电平时间仍达到了600ns,导致乱码。

希望有大佬能帮忙解答
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:688692 发表于 2022-4-3 00:09 | 只看该作者
这个是做WS2812的驱动的吧。单片机指令周期都一两百ns了,用程序来处理肯定来不及了。数据400个一组也就是1.2kb,先存下来再转发内存也不够用。不借助外部硬件恐怕是难。你程序直接中转都达到600ns的原因是因为循环跳转和中断出入的代码。单单MOVE bit指令也要3个周期,就算跑到33MHZ也要100ns所以纯软件是不够的。

其实外部借助两颗二极管就可以了,做个简易的与门。收到前期bit后开通这个与门,这样信号就能通过这个与门向后传递。也就没有速度上的问题了,4148处理1MHz这样的信号问题还是不大的。
回复

使用道具 举报

板凳
ID:624769 发表于 2022-4-4 00:31 | 只看该作者
把  DI 定在  P3.0  DO定在 P3.1, 通过:
CLK_DIV  |=  0x10;
可以直接在 DO 输出 DI 的电平,无需中断,无需任何其他代码,几乎没有延时。
至于你之前说的计数,可以通过 外部中断  EX4  进行计数
来控制打开DO输出:CLK_DIV  |=  0x10;  
还是关闭DO输出: CLK_DIV  &=  0xEF;
回复

使用道具 举报

地板
ID:1015222 发表于 2022-4-6 15:30 | 只看该作者
188610329 发表于 2022-4-4 00:31
把  DI 定在  P3.0  DO定在 P3.1, 通过:
CLK_DIV  |=  0x10;
可以直接在 DO 输出 DI 的电平,无需中断, ...

把DI定在p3.0口后发现输入数据的电平被拉低且会使数据码混乱,导致整体不受控,这是什么原因....
回复

使用道具 举报

5#
ID:624769 发表于 2022-4-7 01:00 | 只看该作者
以梦为舟 发表于 2022-4-6 15:30
把DI定在p3.0口后发现输入数据的电平被拉低且会使数据码混乱,导致整体不受控,这是什么原因....

设置 CLK_DIV |= 0x10;  之后,
P3.1 是 无缝输出 P3.0 电平的,这个过程类似于中继功能是不会有任何差错的(硬件电路问题除外)。检查一下你的外部电路,比如,下载电路是否有断开等等。
作为你的问题“怎么最快将DI口数据赋给DO口?”  就你这个单片机而言,这个方法应该是最快的了,没有之一。

至于,你说的“输入数据的电平被拉低且会使数据码混乱” 我不太理解, 粗看你贴的代码, 也没看出你程序的运行原理,
比如:
1)你 外部中断设定的是 下降沿触发,换句话说只能 DI == 0 的瞬间才触发,你中断里却判断 DI == 1 还是0。 个人认为这是毫无意义的行为。
2)虽然你写了定时期中断, 但是只看到 TR0 = 0 没看到 TR0 =1, 感觉 count = 0 这个操作,对 外部中断没有任何实际作用, 分析不出你贴着两段代码的目的。

最后,你解码必定要有一个接收解析过程吧? 按你说的时序要求,这个解码,或者解析需要严格时间控制的吧? 也没看你在定时器里面解码,所以,诸多不解,看你有什么补充说明再说吧。
回复

使用道具 举报

6#
ID:1015222 发表于 2022-4-8 17:00 | 只看该作者
188610329 发表于 2022-4-7 01:00
设置 CLK_DIV |= 0x10;  之后,
P3.1 是 无缝输出 P3.0 电平的,这个过程类似于中继功能是不会有任何差错 ...

不要求解码,只要转发就行。前面代码有的忘注释掉了,其实就是想表达直接用 DO=DI行不通,加上计数之后就更加不行了。
以下是我根据您说的方式写的代码:
#include <IAP15F105W.h>
/**
  * @brief  IO口初始化
  * @param  null
  * @retval null
  */
//sbit DI = P3^0;
//sbit DO = P3^1;

bit flag;
unsigned int count;//外部中断4计数值
void timer0Init(void)                //300微秒@33.1776MHz
{
        AUXR &= 0x7F;                //定时器时钟12T模式
        TMOD &= 0xF0;                //设置定时器模式
        TL0 = 0xC3;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
}

void main(void)
{
//        P3M0 &= 0XFE;
//        P3M1 |= 0X01;
        CLK_DIV  &=  0xEF;
        count = 0;
  EA=1;
        ET0=1;
        INT_CLKO |= 0x40;
        flag=0;
        while(1)
        {
                if(flag)
                {
                        CLK_DIV  |=  0x10;
                }
        }
}
void int4_Routine(void) interrupt 16
{
        count++;
        if(count>480)
        {
                flag=1;
        }
        TR0=0;
  timer0Init();      //计时300us则复位
}

void Timer0_Routine(void)   interrupt 1  //定时器0中断服务函数
        {
           count = 0;
           flag=0;
        }


根据这个代码可以实现全部转发不乱码,但是计数没有起作用即不能做到前20*24bit不接收,只能做到全部转发。


以上是DI与DO的波形图,两者完全相同。不知道是我的代码有误还是什么原因。。。
回复

使用道具 举报

7#
ID:624769 发表于 2022-4-8 22:07 | 只看该作者
其实不太确定你的意图, 你的计数到底计数的什么, 如果是 计数480个下降沿的话,  是不是应该这样?

void int4_Routine(void) interrupt 16
{
         if(++count==480)
       {
                 CLK_DIV  |=  0x10;
       }
       if(++count>=9600)
      {
               count = 0;
               CLK_DIV  &=  0xEF;
      }
}

这只是一个建议, 因为我没理解你希望的 效果,CLK_DIV 你应该理解为一个 开关,在你认为适当的时候,打开开关 让 P3.1 输出 P3.0 不需要的时候关闭。这个开关只是一个动作,你在某一个中断中 设置 开/关 即可,不需要再while里面反复去操作这个开关。你要知道,STC15系列 即便你 频率定为 35MHz, 1us 可以有35个机器时钟,处理一个if 判断 一个双字节16位数字  需要20个时钟 已经超过 0.5us了,所以,按你的需要你必须把不必要的步骤精简再精简。
回复

使用道具 举报

8#
ID:1015222 发表于 2022-4-12 16:42 | 只看该作者
188610329 发表于 2022-4-8 22:07
其实不太确定你的意图, 你的计数到底计数的什么, 如果是 计数480个下降沿的话,  是不是应该这样?

void i ...

          其实就是想用单片机实现上图表现的效果,不一定要用STC的,当然了,越便宜越好。
回复

使用道具 举报

9#
ID:887371 发表于 2022-4-13 14:32 | 只看该作者
以梦为舟 发表于 2022-4-8 17:00
不要求解码,只要转发就行。前面代码有的忘注释掉了,其实就是想表达直接用 DO=DI行不通,加上计数之后就 ...

把你的代码改了下,应该能做到前20*24bit不接收。
  1. #include <IAP15F105W.h>
  2. bit flag;
  3. unsigned int count = 480;//外部中断4计数值

  4. void timer0Init(void) // 300微秒@33.1776MHz
  5. {
  6.         AUXR &= 0x7F; //定时器时钟12T模式
  7.         TMOD &= 0xF0; //设置定时器模式
  8.         // TL0 = 0xC3;          //设置定时初值
  9.         TH0 = 0xFC;          //设置定时初值
  10.         TF0 = 0;          //清除TF0标志
  11.         TR0 = 1;          //定时器0开始计时
  12. }
  13. void main(void)
  14. {
  15.         CLK_DIV &= 0xEF;
  16.         EA = 1;
  17.         ET0 = 1;
  18.         INT_CLKO |= 0x40;
  19.         flag = 0;
  20.         while (1)
  21.         {
  22.         }
  23. }
  24. void int4_Routine(void) interrupt 16
  25. {
  26.         if (!flag)
  27.         {
  28.                 if (--count == 0)
  29.                 {
  30.                         flag = 1;
  31.                         CLK_DIV |= 0x10;
  32.                 }
  33.         }
  34.         else
  35.         {
  36.                 // TL0 = 0xC3;
  37.                 TH0 = 0xFC; //计时300us则复位
  38.         }
  39. }
  40. void Timer0_Routine(void) interrupt 1 //定时器0中断服务函数
  41. {
  42.         // TL0 = 0xC3;
  43.         TH0 = 0xFC; //计时300us则复位
  44.         flag = 0;
  45.         count = 480;
  46.         CLK_DIV &= 0xEF;
  47. }
复制代码
回复

使用道具 举报

10#
ID:887371 发表于 2022-4-13 14:43 | 只看该作者
你这个需要的mcu要支持双边沿触发中断,并且时钟够快。

现在所有的mcu都能做到。
引脚变化中断了解下。
回复

使用道具 举报

11#
ID:624769 发表于 2022-4-13 21:03 | 只看该作者
以梦为舟 发表于 2022-4-12 16:42
其实就是想用单片机实现上图表现的效果,不一定要用STC的,当然了,越便宜越好。

所以, 你既然 确定了, CLK_DIV |= 0x10 的中继输出 可以满足你的输出需要,(如果输出高电平的驱动力不够,开推挽输出)

你现在唯一需要的就是: 理清楚,前面放弃数据的“过滤条件”, 达到这个条件后, 打开 中继输出 通道,然后达到什么条件以后 再次关闭中继输出, 就是那么简单的一个工作而已。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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