Omapl138双核通信中断及共享内存搬运的代码实现分析(一)

关键部分代码如下所示:

	 // 把sin_buffer这个数组的值写到共享内存上
 	SRBuffer *buffer = SR_buffer_new(SHARED_BUFFER_ADDR, SHARED_BUFFER_SIZE);(1)
    char *data = (char *)SR_buffer_data(buffer);(2)
    memcpy(data, sin_buffer, SR_buffer_size(buffer));(3)

    sleep(2);

    // 产生起始信号,触发DSP的中断
    Trigger *trigger = trigger_new();(4)
    trigger_line(trigger, EVENT_LINE_2_ARM_DSP);(5)
    trigger_destroy(trigger);(6)

    // 等待结束信号,即等待DSP触发ARM的中断
    IRQEvent *event = irq_event_new("/dev/input/c674x_irq_events");(7)
    uint32_t line;
    assert(irq_event_wait(event, &line) && line == EVENT_LINE_0_ARM);
    irq_event_destroy(event);

    // 从共享内存中读出内容,存到freq_buffer数组里
    float freq_buffer[1024];
    memcpy(freq_buffer, data, SR_buffer_size(buffer));
    SR_buffer_destroy(buffer);

#define SHARED_BUFFER_ADDR 0xC2000000
#define SHARED_BUFFER_SIZE 0x1000
首先分析(1): SRBuffer *buffer = SR_buffer_new(SHARED_BUFFER_ADDR, SHARED_BUFFER_SIZE);
SRBuffer是一个结构体,其内容如下:
struct _SRBuffer {
int fd;
void *map_base;
void *data;
uint32_t size;
bool is_bad;
};

SRBuffer *buffer = SR_buffer_new(SHARED_BUFFER_ADDR, SHARED_BUFFER_SIZE);
->
SRBuffer *SR_buffer_new(uint32_t phy_addr, uint32_t size) {
    SRBuffer *buffer = (SRBuffer *)calloc(1, sizeof(SRBuffer));

    buffer->fd = open("/dev/mem", O_RDWR | O_SYNC);
/*/dev/mem是物理内存的全映射,包括整个处理器的地址空间,具体包含地址总线上的所有可寻址空间和IO空间,
但要保证这些物理地址是有效的,可以从这些地址上访问到数据。理论上可以映射0-0xffffffff的地址空间。*/
    if (buffer->fd < 0) {
        fprintf(stderr, "fail to open /dev/mem\n");
        buffer->is_bad = true;
    }
    else {
        off_t addr = phy_addr ;
        off_t off_page = addr & ~(sysconf(_SC_PAGE_SIZE) - 1); 
/*sysconf(_SC_PAGE_SIZE)可以得到一页的大小,一般是4096k,因为映射的地址必须的是一页大小的整数倍,所
以可以通过与0x111取反后做与运算,从而将0-3位清零。假如phy_addr是0xE8000020,经过运算之后就是0xE8000000*/
        buffer->map_base = mmap(NULL,  addr + size - off_page, 
                                PROT_READ | PROT_WRITE, MAP_SHARED, buffer->fd, off_page);
/*mmap就是把一个文件的内容在内存里面做一个映像。映射成功后,用户对这段内存区域的修改可以直接反映到内
核空间,同样,内核空间对这段区域的修改也直接反映用户空间*/                                                    
        if (buffer->map_base == (void *) -1) { 
            fprintf(stderr, "fail to map %x\n", (uint32_t)addr);
            close(buffer->fd); 
            buffer->is_bad = true;
        }

        buffer->data = buffer->map_base  + addr - off_page;
        buffer->size = size;
        buffer->is_bad = false;
    }

    return buffer;
}

再来分析(2) char *data = (char *)SR_buffer_data(buffer);

void *SR_buffer_data(const SRBuffer *buffer) {
    assert(buffer);
    return buffer->is_bad ? NULL : buffer->data;
}

如果前面的映射没有出错的话,就返回buffer->data,以后用户对buffer->data的操作就相当于对
SHARED_BUFFER_ADDR 0xC2000000进行操作。
同理SR_buffer_size(buffer),就是返回要操作的内存的大小。

uint32_t SR_buffer_size(const SRBuffer *buffer) {
    assert(buffer);
    return buffer->is_bad ? 0 : buffer->size;
}

所以(3) memcpy(data, sin_buffer, SR_buffer_size(buffer))就是把size大小(4094byts)的sin_buffer数组的值复制到从SHARED_BUFFER_ADDR 0xC2000000起始的一段地址上。
接下来,通过触发DSP的中断来通知DSP。
Trigger *trigger = trigger_new();(4)

Trigger *trigger_new() {
    Trigger *trigger = (Trigger *)calloc(1, sizeof(Trigger));

    trigger->fd = open("/dev/mem", O_RDWR | O_SYNC);

    if (trigger->fd < 0) {
        fprintf(stderr, "fail to open /dev/mem\n");
        trigger->is_bad = true;
    }
    else {
        off_t addr = SOC_SYSCFG_0_REGS + SYSCFG0_CHIPSIG ;
        uint32_t size = sizeof(uint32_t);
        off_t off_page = addr & ~(sysconf(_SC_PAGE_SIZE) - 1); 

        trigger->map_base = mmap(NULL,  addr + size - off_page, 
                                PROT_READ | PROT_WRITE, MAP_SHARED, trigger->fd, off_page);
        if (trigger->map_base == (void *) -1) { 
            fprintf(stderr, "fail to map %x\n", (uint32_t)addr);
            close(trigger->fd); 
            trigger->is_bad = true;
        }

        trigger->chipsig = trigger->map_base  + addr - off_page;
        trigger->size = size;
        trigger->is_bad = false;
    }

    return trigger;
}

与(1)中分析类似,经过上述初始化后,就可以通过chipsig来对位于01C1 4174h的Chip Signal Register寄存器进行操作。
这个寄存器的描述如下:
DSP可以访问ARM中断映射中的4个ARM中断事件:SYSCFG_CHIPINT0,
SYSCFG_CHIPINT1, SYSCFG_CHIPINT2, and SYSCFG_CHIPINT3。ARM可以访问3个DSP中断事件映射中的中断事件:SYSCFG_CHIPINT2, SYSCFG_CHIPINT3, 和 NMI。
ARM可以通过设置CHIPSIG[3-2]两位中的一个来生成对DSP的中断,或通过设置CHIPSIG[4]生成NMI中断。DSP可以通过设置CHIPSIG[3-0]四位中的一个来中断ARM。将1写入这些位将设置中断,写入0无效。读取返回这些位的值,也可以用作状态位。
在这里插入图片描述
在这里插入图片描述
trigger_line(trigger, EVENT_LINE_2_ARM_DSP);(5)
这个函数的原型是:

void trigger_line(Trigger *trigger, uint32_t line) {
    assert(trigger);
    assert(line < EVENT_LINE_4_DSP_NMI/* unsupported now */);

    if (! trigger->chipsig) {
        fprintf(stderr, "fail to trigger line(%d) by previous error\n", line); 
        return;
    }

    *trigger->chipsig = 1 << line;
}

经过前面的判断之后,最后实际起作用的是:

*trigger->chipsig = 1 << line;

*trigger->chipsig = 1 << EVENT_LINE_2_ARM_DSP;

EVENT_LINE_2_ARM_DSP在这里实际表示的含义是2,它是枚举类型,定义在event_line.h中,

typedef enum {
    EVENT_LINE_0_ARM = 0,
    EVENT_LINE_1_ARM,
    EVENT_LINE_2_ARM_DSP,
    EVENT_LINE_3_ARM_DSP,
    EVENT_LINE_4_DSP_NMI,
    EVENT_LINE_MAX,
} EventLine;

第一个枚举成员的值为整型的0,后续枚举成员的值在前一个成员上加1。
所以执行完(5)之后,就将CHIPSIG2位置1,生成对DSP的中断。
trigger_destroy(trigger);(6)中会解除映射,并释放内存。

void trigger_destroy(Trigger *trigger) {
    if (! trigger)
        return;

	munmap(trigger->map_base, trigger->size); 
    close(trigger->fd);

    free(trigger);//释放内存,与calloc搭配使用
}

以上就完成了触发DSP中断的任务,下面就是ARM等待DSP中断的过程。
IRQEvent *event = irq_event_new("/dev/input/c674x_irq_events");(7)
这里的/dev/input/c674x_irq_events是通过相应的驱动产生的,源文件位于:https://download.csdn.net/download/qq_40788950/10910219 写一个很简单的Makefile即可运行生成
c674x_irq_events.ko文件,然后insmod c674x_irq_events.ko即可添加这个驱动。下一篇文章简单分析一下这个驱动文件。

发布了83 篇原创文章 · 获赞 127 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_40788950/article/details/86305401
今日推荐