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
- 2440 PWM 逻辑编码 记录
- 【记录】PWM
- PWM学习记录
- 【记录】PWM-HBridge
- stm32zet6 PWM 记录
- 记录一下有关PCA的PWM
- 2440PWM应用
- PWM
- PWM
- pwm
- PWM?
- PWM
- PWM
- PWM
- PWM
- PWM
- PWM
- PWM
- Ultra Edit使用技巧
- 发现一个enum的新用法
- 第一个Android下的SDL2.0应用程序
- ubuntu下如何查看软件安装目录以及安装版本 .
- ASP.NET获取MS SQL Server安装实例
- 2440 PWM 逻辑编码 记录
- Object-c中如何打印函数调用栈Object-c中如何打印函数调用栈
- java 面试题
- 网络营销推广108招
- OpenStack Grizzly中的nova-conductor
- 从此乱码是路人
- Python中的*args和**kwargs
- 如何计算MP3的总时长问题(一)
- opacity