2440 PWM 逻辑编码 记录

来源:互联网 发布:淘宝代购港版手机 编辑:程序博客网 时间:2024/05/21 10:54

void timer0_init(void){rGPBCON &=~((3<<0)|(3<<10));rGPBCON|=2<<0|1<<10;//将rGPB0设置为TOUT,PWM定时器输出管脚rGPBUP &=~((1<<0)|(1<<5));//使能上GPB0上拉电阻;rTCFG0&=~(0xff<<0);//prescal=0;rTCFG1&=~(0xf<<0);//divider=2,inputock=PCLK/(prescal+1)/devider                   //=50M/1/2=25M                   //则定时器,没计数一次,时间是1/25us;//step1rTCON&=~(0x1f);//给定时器清个零             rTCNTB0|=960;//给计数器赋计数值rTCMPB0|=360;//给计数器赋值,在这个值一直往下,输出电平翻转rTCON |=1<<1;// 使能手动更新,人工把TCNTB ,TCMPB里面的值加载到TCNT0和TCMP0里面rTCNTB0|=1800;rTCMPB0|=360;//step2rTCON|=1<<0;//开启定时器rTCON&=~0x6;//把人工装载所置位,清零,intevel清零,rTCON|=1<<3;//使能自动装载}

不用中断下,可以精准定时到0.5us一下都可以,代码如上,注意启动定时器后,即 TCON |=1<<1后,要对手动更新位清零,即 rTCON &=~0x2,但是问题是,只能产生一个固定周期的方波,不方便产生任意周期的方波

所以考虑使用中断方式产生固定样式的方波哦,

代码如下,中断服务程序的代码:

void __irq timer0_isr(void){    jakill++;   if((jakill%2)==0)        rGPBDAT &=~(1<<5);   else        rGPBDAT  |=1<<5;           rSRCPND |=1<<10;//rINTPND |=1<<10;//清除timer0的中断rTCNTB0=1000;rTCMPB0=500;rTCON&=~0x6;//把人工装载所置位,清零,intevel清零,rTCON|=1<<3;//使能自动装载}void timer0_isrinit(void){rINTMSK&=~(1<<10);//使能timer0时钟中断,这里时钟中断没有子中断,所以
//不用对子中断进行定义,即 rINTSUBMSK无需定义;pISR_TIMER0=(unsigned int)timer0_isr;//装载中断服务函数}

即可以在中断服务程序里面改变 TCNTB0和TCMPB0的值,以获取不同脉冲宽度的数据

但是问题来了,因为中断服务程序非常长的话,可能下一次中断到来都没有执行完毕中断服务程序,导致没有效果

目前来讲定位到40us是可以的,即TCNTB0=1000,是可以的,不知道最低能多少

现在尝试清楚中断标志位这行代码删除试试!!!试了一下,不清除中断标志是可以的哈,不会一直在中断程序里面吧;

答案是不清除中断,就不行,不能跳转到主函数里面执行。

把中断代码精简了一下,可以跳转到主函数里面执行了,又能够弄到0.5us

代码如下:

void __irq timer0_isr(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除中断标志rTCNTB0=24;rTCMPB0=12;}

现在考虑如何产生一个固定时序的波形

产生上面这样,A8前面的波形。可以使用如下代码:

void __irq timer0_isr(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除timer0的中断jakill++;if(jakill==4){jakill=0;} switch(jakill) { case 0:   {           rTCNTB0=1250;       rTCMPB0=624;       break;   }    case 1:   {           rTCNTB0=624;       rTCMPB0=250;       break;   }    case 2:   {           rTCNTB0=1874;       rTCMPB0=250;       break;   }    case 3:   {            rTCNTB0=2500;       rTCMPB0=250;       break;   }       // case 4:  // {           // rTCNTB0=624;     //  rTCMPB0=0;     //  break;  // } default:;    }   

在中断函数里,会一直产生这样的波形,这样才能被示波器捕捉到,不然只产生一次 就不会捕捉到。但是A8后面的波形,就不那么容易产生了,言外之意,我不能在一个计时循环内,产生没有变化的波形。下面我要试验一下。

实验结果表明。不得行,或许结合,interval位置1或者清零才可以

如果interval 置1, rTCNTB0和rTCMPB0相等的话,出来的波形是一直都是高电平

如果interval 清零, rTCNTB0和rTCMPB0相等的话,出来的波形是一直都是低电平

引导头部分完成了一部分。实际的图形就想下面那样示波器打出来的波形



现在问题来了?帧头可以产生了,如何产生一个数据呢?比如我要发送某某命令。我该如何弄呢?


下面是第二章

在第二章里面,我想把这些产生帧头啊,帧尾啊的数据模块化!比如在第一个中断函数里面,依次通过case实现了帧头的编码

于是乎我想,我可以在帧头结束的时候,把中断函数更换成实现数据编码的东西,这个挺好玩的哈!我的代码是这样的:

void __irq timer0_isr2(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除timer0的中断    jakill++;if(jakill>=2){jakill=0;} switch(jakill) { case 0:   {    rTCON &=~(1<<2);       rTCNTB0=1000;       rTCMPB0=500;       break;   } case 1:   {       rTCNTB0=2000;       rTCMPB0=1000;       break;   }     default:;    }   }void __irq timer0_isr1(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除timer0的中断jakill++;if(jakill==5){jakill=0;} switch(jakill) { case 0:   {    rTCON &=~(1<<2);//关闭翻转位,脉冲开始时是低电平   rTCON&=~(1<<1);       rTCNTB0=625;       rTCMPB0=2;       break;   } case 1:   {        rTCON |=0x6;//设置翻转为,脉冲开始时时低电平     rTCON&=~(1<<1);       rTCNTB0=625;       rTCMPB0=250;       break;   }    case 2:  {       rTCNTB0=1875;       rTCMPB0=250;       break;   }    case 3:  {         rTCNTB0=2500;       rTCMPB0=250;       break;  }       case 4:  {         rTCNTB0=625;        rTCMPB0=2;        pISR_TIMER0=(unsigned int)timer0_isr2;//装载中断服务函数        break;       } default:;    }   }

上面代码中,在第一个中断函数里面,结束的时候,把中断服务函数换掉,具体换成什么函数呢?我的想法是换成在,可以对数据编码的函数

正在思考中。。。

我思考了一下,可以通过在主函数里面,弄启动或者关闭中断函数来实现!!!终极解密哈哈哈哈哈。

思考的过程如下:

比如我要加载一段引导头,我可以写一个子函数,把定时器配置好,把这个中断服务函数设置好,在这个中断服务函数里面,把要处理的时序交代完毕,最后交代完毕后,把定时器关闭!!

我要处理数据,我可以写一个子函数,同样把定时器配置好,把这个中断服务函数设置好,在这个中断服务函数里面,把要处理的数据的时序交代完毕,甚至可以长时间迭代,最后把定时器关闭。

上面都有一个共同的想法在里面:这个想法就是我用到定时器的时候,我开启它,我同时开启它的中断,我不用的时候,我就关闭定时器,等用到的时候再开启!!!

这个思维方式现在来看是很符合逻辑的,下面进行尝试哈。。。

我的本意出发点式好的,现在问题是,如何测试? 如果我写好了帧头的代码,它有主函数里面的设置和辅助函数里面共同完成,那么我是用一个

while(1)

{

yindao();

}

这样的程序来测试,但是结果是相差甚远的,不对!!

哪里出问题了while里面的程序,和中断程序是顺序执行,有可能重叠执行呢,比如当我执行yidao的初始化的时候,中断来了,又或者其他,所以这个是不行的哈,

我得想个招,全局变量,等到中断执行完毕,然后返回到我的while循环体里面,再进行一次其他的设置,我觉得行哦,试试把!

我试了一下,发现不得行,延时1us 结果得到的是34us  ,不仅不在接受范围之内,而且误差太大了 ,我乐个去~~

但是我想到了一个方法

这个方法就是,在中断函数里面,结尾处把下一个要使用的中断函数,改变,比如当前输出为1的中断函数

如果要发送的命令当中下一个是0,则在末尾把它改变成要输出为0的中断函数哈,现在我来试一下。

现在已经基本实现了上述的要求,但是方法不一样!!

方法是:产生帧头的代码和产生数据的代码不同,产生帧头的代码作为一个中断函数,产生数据的代码作为另一个中断服务函数,
在产生帧头的中断服务函数结尾,要把timer0的中断处理函数换成产生数据的中断处理函数。

在数据中断处理函数里面,对要发送的命令判断,如果判断是1,就设置TCNTB0,TCMPB0为相应的值,判断数据为0

就设置这二者为另外的值,实现了预期的目标。下面为代码和产生的波形图:

static  int jakill;volatile  unsigned  char command=0x6;volatile  unsigned char command_cnt=4;void __irq timer0_data(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除timer0的中断      if(command_cnt==0)       {        command_cnt=4;        command=0x6;        }              else      {    command_cnt--;    if(command&0x1)      {            command>>=1 ;             rTCON |=1<<2;//开启翻转位,脉冲开始时是高低电平     rTCON&=~(1<<1);        rTCNTB0=1250;        rTCMPB0=250;       }     else         {         command>>=1;         rTCON |=1<<2;//开启翻转位,脉冲开始时是高低电平         rTCON&=~(1<<1);         rTCNTB0=625;         rTCMPB0=250;                   }       }  //  else   //  {     //  rTCNTB0=625;     //  rTCMPB0=250;      // pISR_TIMER0=(unsigned int)timer0_yindao;//装载中断服务函数        //  }}void __irq timer0_yindao(void){    rSRCPND |=1<<10;//    rINTPND |=1<<10;//清除timer0的中断jakill++;if(jakill==5){jakill=0;} switch(jakill) { case 0:   {    rTCON &=~(1<<2);//关闭翻转位,脉冲开始时是低电平   rTCON&=~(1<<1);       rTCNTB0=625;       rTCMPB0=2;       break;   } case 1:   {        rTCON |=0x6;//设置翻转为,脉冲开始时时低电平     rTCON&=~(1<<1);       rTCNTB0=625;       rTCMPB0=250;       break;   }    case 2:  {       rTCNTB0=1875;       rTCMPB0=250;       break;   }    case 3:  {         rTCNTB0=2500;       rTCMPB0=250;       break;  }       case 4:  {         rTCNTB0=625;        rTCMPB0=2;        pISR_TIMER0=(unsigned int)timer0_data;        break;       } default:;    }}void timer0_isrinit(void){rINTMSK&=~(1<<10);//使能timer0时钟中断,这里时钟中断没有子中断,所以//不用对子中断进行定义,即 rINTSUBMSK无需定义;pISR_TIMER0=(unsigned int)timer0_yindao;//装载中断服务函数}

随意搞出来的command,0x6,用它产生的命令,弄出来的波形。


下面的步骤,如何产生一个命令的完整波形,并使用数据结构搞定。


想到了一个方法:

建立一个结构体,全局变量,command结构体里面

typedef struct commad{

data,

data_cnt

};

data用来存放命令数据,data_cnt用来存放命令数据的位数;

在主函数里面,建立一个数组,保存各种命令,是上面的结构体,

在使用到某一个命令的时候,令全局变量command里面的值编程所需要的命令,再启动定时器。

就OK了

明天来搞一下。

现在想如何进行编码这方面的事情!!早上师兄说,对命令进行编码的时候,先弄一个数组,把要编码的命令,包括crc校验,其他选项,全都放在一个数组里面,在这个数组里面,当我需要发送命令的时候,调用发送程序,进行发送就可以了哈!

这个是原理的部分!!

最重要的,师兄说,公司的读卡器选择的Tari值是6.25us,PW值是3.125us,我的神,这个我的开发板能够达到这个级别么,小于3us的时候会产生重复的波形啊,奶奶的!!

刚才小沈说。arm  cpu能跑的快,全赖 HCLK,好,我把HCLK改高一点,试试!!

把姿态放低,这个东西还是值得你去思考的哈!;



东哥的想法是:

1用两个定时器,一个是timer0 ,中断里面什么都不做

2另外一个设置基准定时进入中断,比如0.124us进入中断。

在第二个定时器进入中断的时候,计数,来设置定时器1里面的TCNTB0和TCMPB0,这样一直计数,就可以了哈

3哦哈哈哈明天试一试

如果要产生4位的数据,那么进入数据中断的次数是五次:

第一次进入数据中断的时候,是把主函数里面设置的TCNTB0加载到内部寄存器,产生的波形不是数据,这一次进入中断,会把下第一位数据的TCNTB0设置好。

第二次进入中断,更新上一次中断设置的值,产生第一位数据。同时设置TCNTB0为第二位数据值

第三次中断发生时,更新上一次中断设置的TCNTB0,产生第二次中断,同时设置TCNTB0为第三位数据值

第四次中断发生,更新第三次中断设置的TCNTB0值,产生第三位数据,同时设置TCNTB0为第四位数据值

第五次中断发生,则更新上一次设置的TCNTB0值,产生第四位数据!!!问题来了,这一次产生中断后,需要弄点什么呢?

如果什么都不做。则 第六次中断发生的时候,立马关闭定时器,第五位的数据就不会产生么?这样产生的也就是完完整整的数据。



貌似使用东哥的方法,重复产生的波形,更多!!!这是什么道理

使用东哥的方法,结合田氏的想法。只使用一个中断,即定时器1的中断,在中断函数里面,给定时器1和0赋值同样的TCNTB和TCMTB,这样,两个定时器来临中断的时间差就会固定,然后就能保证定时器1的中断来临一定会在定时器0前面,哈哈。下面是代码;

void __irq timer1_data(void){    rINTPND |=1<<11;//清除timer1的中断    rSRCPND |=1<<10;//       // rINTMSK=1<<11;   switch(jakill)   {   case 0:       {       rTCNTB0=625;       rTCMPB0=312;       rTCNTB1=625;       jakill=1;       break;       }   case 1:       {       rTCNTB0=312;       rTCMPB0=156;       rTCNTB1=312;       jakill=0;       break;       }     }      

可以看到波形在3us的波形原本我想法是产生一个,但是实际上产生了两个!!!!

于是乎我问韦东山群里面的人,如果中断没处理完,又产生一个本中断,会产生什么结果,答案是中断不会嵌套!!

中断不会嵌套的含义是,当前中断函数如果没有处理完毕的话,如果再产生一个中断,就会不接受这个中断,继续本中断,知道本中断处理完毕,才会接受下一个中断。

现在来分析一下上面的波形,在产生后面的3.5us的波形的时候,定时器的定时时间被改短了,但是我的中断服务函数还没有处理完毕,于是当定时器产生中断的时候,我把它给拒绝了,它就利用上一次赋值的3us的TCNTB0值,再进行一次装载,再产生一次3us的波形,在产生第二次波形的中间的某一个时间点,中断处理函数已经给TCNTB0赋值为12us,所以中断来临后,就产生12.5us的波形,然后再中断服务函数里面给TCNTB赋值3us的值,这是会具有充足的时间来赋值的,下一次中断来临,产生3us的波形,中断服务程序开始执行,但是中断服务程序没执行完,又产生中断,于是又产生3us的波形,知道中断处理完毕。。。。所以估计中断处理的时间在6us到12us之间!


现在分析一下这个结果哈:

从表面上得到的结果,是CPU时间处理不过来中断服务函数,但是实际上呢,昨天用东哥的测试代码测试出来cpu的频率在25M,就相当于外部晶振,这个不合理啊,配置的时候没有配置错误啊?

而且我还有个疑问:定时器是50M,FCLk是200M,加上五级流水线,定时器计数50次,我的反汇编后面的代码有30行,每一行×5是150/4约等于50,这就说明了,还是不行,cpu确实是跑不过定时器,不是cpu频率不够的原因吧?

确实是cpu没有跑起来!!!因为2440和外设存储器之间的关系,每次都要从外围存储器取指令,执行,速度当然跟不上!!

所以要允许cache来进行缓存!!!这样就可以大大提高cpu的运行速度。实际情况是

进行mmu设置之后,跑3.25us的波形无压力了哈!!!

好吧,这么容易就搞定了吗,太简单了吧。但是 我还不太了解MMU的组成原理,要仔细阅读以下才行

下面是代码:

int Main(){  unsigned long a = 1000000;MMU_Init();timer0_isrinit();Uart0_Init(115200) ;timer0_init();while(1){ Uart0_Printf("Uart0_Printf test output is:%d\n",a) ;} return 0;}

下面是图片


图上可以看到3.25us的东东无压力了哦


需要注意的一点,定时启动的流程是:

1.先给TCNTB0 TCMPB0 赋值

2.手动更新TCNTB0等到内部寄存器

3开启定时器

4关闭按手动清零位

5开启自动更新


必须按着这个步骤来,不然出错都不知道从什么地方找

还有就是代码如果不对,就可以格式化一下nandflash

还有个问题

在MMU里面直接开ICACHE ,DCACHE,不用内存映射,当程序执行错误的时候,这一条可以解决很多问题

同学的代码,中断服务程序都是好的。但是就是不能再中断里面改变寄存器的值,想了一下是这个原因!

因为开启了MMU,而且在MMU里面进行了地址映射,所以有一些 寄存器的实际地址被映射过头了,然后就会产生意想不到的效果,你对这个寄存器赋值,会发现赋完值之后,这个寄存器的值并没有改变,这是一件很令人头疼的事情。

解决办法,就是在MMU_Init()函数里面,只开启icache和dcache,而不进行地址映射,这样就会避免对寄存器赋值无效的操作了。

经过比较,不用MMU_Init(),不能精准定时到3.2us,用了之后无压力的哈

所谓cache的作用,就是作为一个缓存,我cpu写一个东西到内存,如果不开启cache的话,那么直接写到内存,在等待回执,就比较慢,而开启了cache之后呢,我可以直接写进cache,cpu可以不管你,继续运行,有cache来写进内存,这样会有个问题,等到我cpu写进同一个寄存器的值改变的时候,我的cache才会写进内存,不然一直不会改变

而且cache不能存储太多数据和程序,小块的程序还可以运行。


MMU,不进行内存映射,速度不能达到1us

进行内存映射呢?试一下哈还是不能定时到1us,就是说波形里面如果有1us的输出的话,会产生重复波形

路途漫漫啊 ,想定时到1us怎么就这么难呢,哎!!

下午把FCLK提高到400M试试

400M下,使用MMU,能达到3us和2us轮流交替不重复

但是在2us和1us的时候,果断就重复了,这个是什么道理啊,我热。

公司的读卡器 ,只解码FM2编码,即MILLER2编码


陈师兄说,MILLER2编码的时候。一个数据0是高低高低,数据0的总长度是2Tari,不管它的数据速率的问题了哦

就是说一个高低电平就是一个Tari

原创粉丝点击