On the Raspberry Pi, which is supposed to be used on Linux, the user's program naturally runs under the OS scheduler. Therefore, it is very difficult to write a time-strict program. To put it simply, I'm not good at processing something exactly at regular intervals. In such cases, timer interrupts or clock polling are used, but I think that there are many cases where you want to use the latter because of the low overhead and the simplicity of the context. At that time, various interrupts get in the way. I thought it would be nice if I could prohibit some simple temporary interrupts, and I tried various things, so I wrote an article. It is a Z80 DI / EI that is familiar to old men.
I wrote a program (described later) that polls gettimeofday ()
and runs 100 us periodically. Here are the results (measured cycle) for the standard case and when interrupts are disabled.
how is it? It's obvious at a glance. If interrupts are disabled, it seems that various timing-critical controls can be performed. For example, Infrared remote control. (Note: Of course, disabling interrupts has many side effects. Minimize the disabling time.)
According to the manual of SoC BCM2835 / 2837 of Raspberry Pi, there seems to be "interrupt disable register" and "interrupt enable register", so hit them directly. That is the familiar / dev / mem
in GPIO tommap ()
.
mmap ()
so that you can hit the physical address directly.And this is all. In terms of Z80, 2-3 is DI
and 5 is ʻEI`.
The code used to create the data for the graph above. Please change the part of #define PERI_BASE PI? _PERI_BASE
according to your Raspberry Pi.
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>
#define BLOCK_SIZE (4 * 1024)
#define PI2_PERI_BASE 0x3f000000
#define PI1_PERI_BASE 0x20000000
#define PERI_BASE PI1_PERI_BASE // Raspberry 0/1 or 2/3
//Specified time(us)Wait by polling
inline static void waitUs(struct timeval tv_zero, int until_us) {
struct timeval tv;
int us;
while (1) {
gettimeofday(&tv, NULL);
us = (tv.tv_sec - tv_zero.tv_sec) * 1000000 + (tv.tv_usec - tv_zero.tv_usec);
if (us >= until_us) break;
}
}
int main() {
int mem_fd;
char *map;
int i;
int us_next;
struct timeval tv0, tv1;
int dt[10000];
int irq1, irq2, irq3; //Let's save interrupt information(Finally write back to irqen)
volatile unsigned int *irqen1;
volatile unsigned int *irqen2;
volatile unsigned int *irqen3;
volatile unsigned int *irqdi1;
volatile unsigned int *irqdi2;
volatile unsigned int *irqdi3;
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) return -1;
map = (char*) mmap(NULL,
BLOCK_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
mem_fd,
PERI_BASE + 0xb000
);
if (map == MAP_FAILED) return -1;
irqen1 = (volatile unsigned int *) (map + 0x210);
irqen2 = (volatile unsigned int *) (map + 0x214);
irqen3 = (volatile unsigned int *) (map + 0x218);
irqdi1 = (volatile unsigned int *) (map + 0x21c);
irqdi2 = (volatile unsigned int *) (map + 0x220);
irqdi3 = (volatile unsigned int *) (map + 0x224);
gettimeofday(&tv0, NULL);
irq1 = *irqen1;
irq2 = *irqen2;
irq3 = *irqen3;
//Disable all interrupts on the following 3 lines
*irqdi1 = 0xffffffff;
*irqdi2 = 0xffffffff;
*irqdi3 = 0xffffffff;
for (i = 0, us_next = 100; i < 10000; i++, us_next += 100) {
waitUs(tv0, us_next);
gettimeofday(&tv1, NULL);
dt[i] = (tv1.tv_usec - tv0.tv_usec) + (tv1.tv_sec - tv0.tv_sec) * 1000000;
}
//Restore the interrupt enable flag on the following three lines.
*irqen1 = irq1;
*irqen2 = irq2;
*irqen3 = irq3;
//Result display
for (i = 1; i < 10000; i++) {
printf("%d\n", dt[i] - dt[i - 1]);
}
return 0;
}
Recommended Posts