关键部分代码如下所示:
// 把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即可添加这个驱动。下一篇文章简单分析一下这个驱动文件。