上一篇博客为了实现延时特定时间(4ms)并在这段时间内产生PWM波形,方法是通过计算PWM的单次循环时间(PWM的周期),然后计算出循环次数,使用计数器计数,每次循环判断计数器的值是否等于循环次数。这种方法比较简单,对于不熟悉PRU 的我来说比较好用,对于只改变占空比不改变周期,延时时间不变的波形很容易实现,因为PWM的周期和延时是不变的,所以循环次数也不会改变,轮询检测就搞定了。   但是随着实验的进行,我发现仅仅变占空比满足不了需求,虽然延时时间并没有改变(还是4ms),但是每经过4ms,周期变化了,所以需要改变循环次数,虽然复杂了点还是可以用上述方法解决的,最终打败我的是因为计算的循环次数不一定是整数,每次取整就会带来一定的误差(虽然很小)。   想了想,因为单片机是有两个定时器T0和T1,可以通过超时中断来实现产生特定时间的PWM波形,那么beaglebone的PRU是否也提供了类似的定时器呢,没办法只能继续啃am335的手册,终于发现了Industrial Ethernet Peripheral (IEP)。   (IEP) is intended to do the hardware work required for industrial ethernetfunctions. The IEP module features an industrial ethernet timer with eight compare events. IEP旨在完成工业以太网所需的硬件工作功能。iep模块具有一个工业以太网定时器,具有八个比较事件。默认频率是200MHZ,意味着执行一条指令的时间是5ns。其余详细的细节参考am335x PruReferenceGuide。下面详细介绍代码实现:  

#include <stdio.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <iostream>
#include "prussdrv.h"
#include <pruss_intc_mapping.h>
 
 
#define DELAY_US 4000u     // 4000us 延时 最大值为 21474836 us
#define TICKS ((DELAY_US / 5) * 1000)    // 单条指令5ns,转换成系统滴答数
#define PRU_NUM 0            // 使用PRU0
using namespace std;    
int main(void)
{
        tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
        prussdrv_init();
        prussdrv_open(PRU_EVTOUT_0);
        prussdrv_pruintc_init( &pruss_intc_initdata);
 
        static void *pru0DataMemory;
        static unsigned int *pru0DataMemory_int;
        prussdrv_map_prumem(PRUSS0_PRU0_DATARAM, &pru0DataMemory);
        pru0DataMemory_int = (unsigned int *) pru0DataMemory;
 
        // 数据写入PRU内核空间
        unsigned int sampletimestep = TICKS;      // 4ms的滴答数
        *(pru0DataMemory_int) = sampletimestep;
        unsigned int numbersamples = 10;          // 延时因子暂设,会大约11us周期的波   
        *(pru0DataMemory_int+1) = numbersamples;
 
        // PRU开始时间
        struct timeval start;
        gettimeofday(&start,NULL);
 
        prussdrv_exec_program (PRU_NUM, "./PRU_industrialEthernetTimer.bin");
        prussdrv_pru_wait_event (PRU_EVTOUT_0);
 
        // pru结束时间
        struct timeval end;
        gettimeofday(&end,NULL);
 
        double diff;
        diff = end.tv_sec -start.tv_sec + (end.tv_usec - start.tv_usec)*0.000001;
        cout<< "EBB PRU程序已完成,历时约 "<< diff << "秒!" << endl;
 
        // prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
        prussdrv_pru_disable(PRU_NUM);
        prussdrv_exit ();
        return 0;
}

 

.origin 0
.entrypoint start
 
#define PRU0_R31_VEC_VALID 32    
#define PRU_EVTOUT_0    3        // 完成事件为3
#define IEP  0x2E000             // 使用0x2E000常量寄存器 作为 IEP 寄存器
 
start:
    MOV	r0, 0x00000000           
	LBBO	r1, r0, 0, 4        // r1 寄存器保存滴答数
	MOV	r0, 0x00000004          
	LBBO	r10, r0, 0, 4       // r10 寄存器保存延迟因子 
    
    // 定时器timer需要设置这三位,具体可参考手册
    // GLOBAL_CONFIG 、
    // 0x1 to enable、
    // 0x10 to set default increment、
    // 0x100 to set compensation increment、 compensation is not used here
    mov r7, 0x111             // 当把这个值写入IEP从0开始的4个偏移 就会使能定时器
 
    mov r5,IEP                // r5保存IEP的地址
    ldi r6,0                  // 当把0写入IEP从0开始的4个偏移 就会停止定时器
    ldi r3,0                  // IEP的初值设为0
 
    sbbo r3,r5,r6,4           // 停止定时器
    ldi r6,0xC                // 当把1写入IEP从0xC开始的4个偏移 就会清除所有位
    ldi r4,1
    sbbo r4,r5,r6,4           // 清除所有位
 
TIMERSTART:
    sbbo r7,r5,0,4            // 设能IEP并开始计数
 
PWMCONTROL:
	MOV	r9, 50                // 50的占空比
	SET	r30.t5	              // 输出引脚 P9_27 high
 
SIGNAL_HIGH:
	MOV	r0, r10               // 延迟因子
 
DELAY_HIGH:
	SUB	r0, r0, 1
	QBNE	DELAY_HIGH, r0, 0 
	SUB	r9, r9, 1
	QBNE	SIGNAL_HIGH, r9, 0  // 延迟高电平
 
	MOV	r9, 50                // 50的占空比
	CLR	r30.t5	              // 输出引脚 P9_27 low
 
SIGNAL_LOW:
	MOV	r0, r10               // 延迟因子
 
DELAY_LOW:
	SUB	r0, r0, 1
	QBNE	DELAY_LOW, r0, 0
	SUB	r9, r9, 1
	QBNE	SIGNAL_LOW, r9, 0  // 延迟低电平
 
    lbbo r8,r5,0xC,4           // 获取timer计数器的值
    QBLT	PWMCONTROL, r1, r8  // 执行PWMCONTROL, 除非 超时
 
TIMERSTOP:
    sbbo r3,r5,0,4              // 停止timer计数器
    sbbo r3,r5,0xC,4            // 使计数器的数据为0
 
 
END:
	MOV	R31.b0, PRU0_R31_VEC_VALID | PRU_EVTOUT_0
	HALT

  程序输出结果如下,考虑到进程的切换和寄存器的赋值,时间是能对的上的,然而使用示波器来观察的时候,使用示波器的Normal模式,然后执行程序,发现PWM只发送了2ms左右,查阅资料发现是因为示波器的存储深度选择了自动,采样率设置成了最高,因此示波器最多只能捕捉大约2ms的波形,点击acquire,调低采样率,再次执行即可。