Last active
November 7, 2022 02:15
-
-
Save rlcamp/c9a3e34f162aa86719feb2b531eb5ad5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* blink the LED on the pi zero 2W using /dev/gpiomem, and best-effort realtime timing */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/mman.h> | |
#define NOPE(...) do { fprintf(stderr, "error: " __VA_ARGS__); exit(EXIT_FAILURE); } while(0) | |
#define PIN 29 | |
#define BCM2835_GPFSEL0 0x0000 | |
#define BCM2835_GPSET0 0x001c | |
#define BCM2835_GPCLR0 0x0028 | |
static volatile uint32_t * gpio_base; | |
static void setpin(uint8_t pin, unsigned char state) { | |
__sync_synchronize(); | |
*(gpio_base + (state ? BCM2835_GPCLR0 : BCM2835_GPSET0) / 4 + pin / 32) = 1U << (pin % 32); | |
__sync_synchronize(); | |
} | |
static void set_pin_to_output(uint8_t pin) { | |
/* function selects are 10 pins per 32 bit word, 3 bits per pin */ | |
volatile uint32_t * const address = gpio_base + BCM2835_GPFSEL0 / 4 + pin / 10; | |
const uint8_t shift = (pin % 10) * 3; | |
const uint32_t mask = 0x3 << shift; | |
const uint32_t value = 0x1 << shift; | |
__sync_synchronize(); | |
uint32_t register_value = *address; | |
register_value = (register_value & ~mask) | (value & mask); | |
*address = register_value; | |
__sync_synchronize(); | |
} | |
static int one_interval_has_elapsed(const unsigned long long interval_nanoseconds) { | |
struct timespec timespec; | |
#ifdef CLOCK_MONOTONIC | |
clock_gettime(CLOCK_MONOTONIC, ×pec); | |
#else | |
clock_gettime(CLOCK_REALTIME, ×pec); | |
#endif | |
const unsigned long long now = timespec.tv_sec * 1000000000ULL + timespec.tv_nsec; | |
static unsigned long long prev = (unsigned long long)-1; | |
if ((unsigned long long)-1 == prev) prev = now - interval_nanoseconds; | |
if (now - prev >= interval_nanoseconds) { | |
prev += interval_nanoseconds; | |
return 1; | |
} | |
const unsigned long long delay = prev + interval_nanoseconds - now; | |
nanosleep(&(struct timespec) { | |
.tv_sec = delay / 1000000000ULL, | |
.tv_nsec = delay % 1000000000ULL | |
}, NULL); | |
return 0; | |
} | |
int main(void) { | |
const int fd = open("/dev/gpiomem", O_RDWR | O_SYNC); | |
if (-1 == fd) NOPE("%s: open: %s\n", __func__, strerror(errno)); | |
gpio_base = mmap(NULL, getpagesize(), (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0); | |
if (MAP_FAILED == gpio_base) NOPE("%s: mmap: %s\n", __func__, strerror(errno)); | |
mlockall(MCL_CURRENT | MCL_FUTURE); | |
set_pin_to_output(PIN); | |
while (1) { | |
setpin(PIN, 1); | |
while (!one_interval_has_elapsed(50000000)); | |
setpin(PIN, 0); | |
while (!one_interval_has_elapsed(950000000)); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment