CS107e libpi source files
/*
* Functions for Raspberry Pi ARM Timer.
*
* Author: Pat Hanrahan <[email protected]>
* Dawson Engler <[email protected]>
*
* Date: April 20, 2016
*/
#include "armtimer.h"
#include <stdint.h>
/*
* See BCM p 196 (unfortunately the documentation is wrong in several places)
*
* This timer has two parts: a free running counter and a count-down timer.
* The timer is always running in free running mode.
*
* In this module, we implement the count-down timer. Interval is set as
* load/re-load, will count down from there to zero. At zero, will report
* overflow and generate interrupt (if interrupts enabled)
*
* The timer and counter are based on the APB clock, which on the
* Raspberry Pi is 250 Mhz
*
* Some errors in the documentation:
* (1) the counters are either 32-bit (not 23-bit) or 16-bit
* (2) the effect of pre-scale bits (3:2) in the control register is unclear.
*/
struct armtimer_t {
uint32_t load; // writing immediately loads the counter
uint32_t value; // current counter value, read only
struct {
uint32_t : 1,
timer_is_32_bit: 1, // 0 -> 16-bit, 1 -> 32 bit
: 3,
enable_timer_interrupt: 1, // 1 = timer interrupts enabled
: 1,
enable_timer: 1, // 1 - timer enabled
run_in_debug: 1, // 1 = run timer in arm debug mode/halt
free_counter_enable: 1, // 1 = free running counter enabled
: 6,
free_counter_prescale: 8; // prescalar for the free running counter
// clk/(prescale+1)
} control;
uint32_t clear_event; // clear overflow/interrupt, write only
uint32_t overflow; // overflow/interrupt pending, read only
uint32_t irq; // pending & interrupt enabled, read only
uint32_t reload; // value to be reloaded when value hits 0
uint32_t prescale; // prescaler for countdown timer clk/(prescale+1)
uint32_t free_counter_value; // free running counter value
};
#define ARMTIMER_BASE 0x2000B400;
static volatile struct armtimer_t * const armtimer = (struct armtimer_t *)ARMTIMER_BASE;
/* Cute hack: compile-time assertion . */
#define AssertNow(x) switch(1) { case (x): case 0: ; }
/*
* initialize armtimer
*
* ticks is the number of usecs between overflows/interrupts
*
* timer is initialized, but not enabled. interrupts are disabled.
*
*/
void armtimer_init(unsigned int ticks) {
// make sure bit-fields are within 1 word
AssertNow(sizeof armtimer->control == 4);
armtimer->control.enable_timer = 0;
armtimer->control.enable_timer_interrupt = 0;
armtimer->clear_event = 0;
armtimer->control.timer_is_32_bit = 1;
// setup up timer it counts once per microsecond
armtimer->prescale = (250 - 1); // 250 000 000 / 250 = 1 000 000 = 1 usec
// ticks are number of microseconds between overflows/interrupts
armtimer->load = ticks - 1; // load value is immediately loaded into the counter
armtimer->reload = ticks - 1; // reload value is loaded into the counter when it hits 0
}
/*
* set timer prescalar (apbclock/divisor)
*
* the timer counts at a rate equal to the ABP clock divided by the divisor.
* the APB clock on the Raspberry Pi runs at 250 Mhz. thus, if the divisor
* equals 250, the timer will count once per usec.
*
* The prescale register is 10-bits
*
* The reset value of this register is 0x7D so gives a divide by 126.
*/
__attribute__((unused)) static void armtimer_set_prescalar(unsigned int divisor) {
armtimer->prescale = divisor - 1;
}
void armtimer_enable(void) {
armtimer->control.enable_timer = 1;
}
void armtimer_disable(void) {
armtimer->control.enable_timer = 0;
}
void armtimer_enable_interrupts(void) {
armtimer->control.enable_timer_interrupt = 1;
}
void armtimer_disable_interrupts(void) {
armtimer->control.enable_timer_interrupt = 0;
}
unsigned int armtimer_get_count(void) {
return armtimer->value;
}
bool armtimer_check_overflow(void) {
return armtimer->overflow != 0;
}
void armtimer_clear_event(void) {
armtimer->clear_event = 1;
}
bool armtimer_check_and_clear_overflow(void) {
bool had_event = armtimer_check_overflow();
if (had_event) {
armtimer_clear_event();
}
return had_event;
}
bool armtimer_check_interrupt(void) {
return armtimer->irq != 0;
}
bool armtimer_check_and_clear_interrupt(void) {
bool had_event = armtimer_check_interrupt();
if (had_event) {
armtimer_clear_event();
}
return had_event;
}
/*
* Assembly routine to count leading zero bits
* Author: Philip Levis
*/
.global count_leading_zeroes
count_leading_zeroes:
clz r0, r0
bx lr
/*
* Access to font pixel data stored as a bitmap.
*
* Author: Philip Levis <[email protected]>
* Last modified: 3/17/16
*/
#include "font.h"
typedef struct {
unsigned char first_char, last_char;
size_t glyph_width, glyph_height;
unsigned char pixel_data[];
} font_t;
static const font_t font_default;
static const font_t *g_font = &font_default;
size_t font_get_glyph_height(void) {
return g_font->glyph_height;
}
size_t font_get_glyph_width(void) {
return g_font->glyph_width;
}
size_t font_get_glyph_size(void) {
return font_get_glyph_width() * font_get_glyph_height();
}
/* Extract glyph pixels for requested character from font bitmap.
* Read bits from font bitmap and store into array of bytes, one byte per pixel.
* Use 0xff byte for on pixel, 0x0 for off pixel.
*/
bool font_get_glyph(char ch, unsigned char buf[], size_t buflen) {
if ((ch != ' ' && (ch < g_font->first_char || ch > g_font->last_char)) || (buflen != font_get_glyph_size())) {
return false;
}
if (ch == ' ') { // Handle space as special case, return all-off image
for (int i = 0; i < buflen; i++) {
buf[i] = 0;
}
} else {
int index = 0;
int nbits_in_row = (g_font->last_char - g_font->first_char + 1) * font_get_glyph_width();
int x_offset = (ch - g_font->first_char);
for (int y = 0; y < font_get_glyph_height(); y++) {
for (int x = 0; x < font_get_glyph_width(); x++) {
int bit_index = y * nbits_in_row + x_offset * font_get_glyph_width() + x;
int bit_start = bit_index / 8;
int bit_offset = bit_index % 8;
// extract single bit for this pixel from bitmap
int val = g_font->pixel_data[bit_start] & (1 << (7 - bit_offset));
// use 0xff for on pixel, 0x0 for off pixel
buf[index++] = val != 0 ? 0xFF : 0x00;
}
}
}
return true;
}
/*
* Apple II font stored as a bitmap.
* Each character is 14 bits wide and 16 bits tall (long story
* about some C conversion).
*
* Generated from a screenshot of the original font, turned into
* a 32-bit color C structure with GIMP, then turned into a bitmap
* C structure with a simple C program.
*/
static const font_t font_default = {
.first_char = 0x21, .last_char = 0x7F,
.glyph_width = 14, .glyph_height = 16,
.pixel_data = {
0x03, 0x00, 0x33, 0x00, 0xcc, 0x00, 0xc0, 0x3c,
0x00, 0x30, 0x00, 0x30, 0x00, 0xc0, 0x03, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xf0, 0x03, 0x00, 0x3f, 0x03,
0xff, 0x00, 0x30, 0x3f, 0xf0, 0x0f, 0xc3, 0xff,
0x03, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x30, 0x00, 0xfc, 0x03, 0xf0,
0x03, 0x00, 0xff, 0x00, 0xfc, 0x0f, 0xf0, 0x3f,
0xf0, 0xff, 0xc0, 0xff, 0x0c, 0x0c, 0x0f, 0xc0,
0x00, 0xc3, 0x03, 0x0c, 0x00, 0x30, 0x30, 0xc0,
0xc0, 0xfc, 0x0f, 0xf0, 0x0f, 0xc0, 0xff, 0x00,
0xfc, 0x0f, 0xfc, 0x30, 0x30, 0xc0, 0xc3, 0x03,
0x0c, 0x0c, 0x30, 0x30, 0xff, 0xc3, 0xff, 0x00,
0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
0x03, 0x03, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00,
0xc0, 0x3f, 0x00, 0x3c, 0xc0, 0x00, 0x00, 0xc0,
0x0c, 0xc0, 0x33, 0x00, 0x30, 0x0f, 0x00, 0x0c,
0x00, 0x0c, 0x00, 0x30, 0x00, 0xc0, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfc, 0x00, 0xc0, 0x0f, 0xc0, 0xff, 0xc0,
0x0c, 0x0f, 0xfc, 0x03, 0xf0, 0xff, 0xc0, 0xfc,
0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
0x00, 0x0c, 0x00, 0x3f, 0x00, 0xfc, 0x00, 0xc0,
0x3f, 0xc0, 0x3f, 0x03, 0xfc, 0x0f, 0xfc, 0x3f,
0xf0, 0x3f, 0xc3, 0x03, 0x03, 0xf0, 0x00, 0x30,
0xc0, 0xc3, 0x00, 0x0c, 0x0c, 0x30, 0x30, 0x3f,
0x03, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0x3f, 0x03,
0xff, 0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc3, 0x03,
0x0c, 0x0c, 0x3f, 0xf0, 0xff, 0xc0, 0x00, 0x0f,
0xfc, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0xc0, 0x00, 0x03, 0x00, 0x00, 0xc0, 0x00, 0xc0,
0xc0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x30, 0x0f,
0xc0, 0x0f, 0x30, 0x00, 0x00, 0x30, 0x03, 0x30,
0x0c, 0xc0, 0x3f, 0xc3, 0xc3, 0x0c, 0xc0, 0x03,
0x00, 0x30, 0x00, 0x0c, 0x0c, 0xcc, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0,
0xc0, 0xf0, 0x0c, 0x0c, 0x00, 0x30, 0x0f, 0x03,
0x00, 0x03, 0x00, 0x00, 0x30, 0xc0, 0xc3, 0x03,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0xc0, 0x30, 0x30, 0xc0, 0xc0, 0xcc, 0x0c, 0x0c,
0x30, 0x30, 0xc0, 0xc3, 0x00, 0x0c, 0x00, 0x30,
0x00, 0xc0, 0xc0, 0x30, 0x00, 0x0c, 0x30, 0xc0,
0xc0, 0x03, 0xcf, 0x0c, 0x0c, 0x30, 0x30, 0xc0,
0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0x0c, 0x03,
0x03, 0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc3, 0x03,
0x00, 0x0c, 0x3c, 0x00, 0xc0, 0x00, 0x0f, 0x00,
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00,
0x00, 0x00, 0x00, 0xc0, 0x00, 0x03, 0x0c, 0x00,
0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x3c, 0x0c,
0xf0, 0x33, 0x30, 0x0c, 0x00, 0xcc, 0x03, 0x30,
0x0f, 0xf0, 0xf0, 0xc3, 0x30, 0x00, 0xc0, 0x0c,
0x00, 0x03, 0x03, 0x33, 0x00, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x30, 0x3c,
0x03, 0x03, 0x00, 0x0c, 0x03, 0xc0, 0xc0, 0x00,
0xc0, 0x00, 0x0c, 0x30, 0x30, 0xc0, 0xc0, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x0c,
0x0c, 0x30, 0x30, 0x33, 0x03, 0x03, 0x0c, 0x0c,
0x30, 0x30, 0xc0, 0x03, 0x00, 0x0c, 0x00, 0x30,
0x30, 0x0c, 0x00, 0x03, 0x0c, 0x30, 0x30, 0x00,
0xf3, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0xc0,
0xc3, 0x03, 0x0c, 0x0c, 0x03, 0x00, 0xc0, 0xc3,
0x03, 0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc0, 0x03,
0x0f, 0x00, 0x30, 0x00, 0x03, 0xc0, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x30, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xc0, 0x03, 0x00, 0x0f, 0x03, 0x3c, 0x0c,
0xcc, 0x03, 0x00, 0x33, 0x03, 0xff, 0x0c, 0xc0,
0x00, 0xc0, 0xcc, 0x00, 0x30, 0x0c, 0x00, 0x00,
0x30, 0x3f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0c, 0x0c, 0x3c, 0x03, 0x00, 0x00,
0xc0, 0x0c, 0x03, 0x30, 0x3f, 0xc0, 0xc0, 0x00,
0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x0c, 0x00, 0x30,
0x03, 0x00, 0x3f, 0xf0, 0x03, 0x00, 0x0c, 0x0c,
0xcc, 0x30, 0x30, 0xc0, 0xc3, 0x00, 0x0c, 0x0c,
0x30, 0x00, 0xc0, 0x03, 0x00, 0x0c, 0x0c, 0x03,
0x00, 0x00, 0xc3, 0x30, 0x0c, 0x00, 0x33, 0x30,
0xf0, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0xc0,
0xc3, 0x00, 0x00, 0xc0, 0x30, 0x30, 0xc0, 0xc3,
0x03, 0x03, 0x30, 0x0c, 0xc0, 0x03, 0x03, 0xc0,
0x03, 0x00, 0x00, 0xf0, 0x0c, 0x00, 0x00, 0x00,
0x30, 0x0f, 0xc0, 0xff, 0x00, 0xff, 0x03, 0xfc,
0x0f, 0xc0, 0x30, 0x00, 0xfc, 0x0f, 0xf0, 0x0f,
0x00, 0x0f, 0x03, 0x03, 0x00, 0xc0, 0x3c, 0xf0,
0xff, 0x00, 0xfc, 0x0f, 0xf0, 0x0f, 0xf0, 0xcf,
0xc0, 0xff, 0x0f, 0xf0, 0x30, 0x30, 0xc0, 0xc3,
0x03, 0x0c, 0x0c, 0x30, 0x30, 0xff, 0xc0, 0xf0,
0x00, 0xc0, 0x03, 0xc0, 0x00, 0x00, 0xcc, 0x00,
0xc0, 0x0c, 0xc0, 0xff, 0xc3, 0x30, 0x00, 0x30,
0x33, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x0c, 0x0f,
0xc0, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x03, 0x0f, 0x00, 0xc0, 0x00, 0x30, 0x03,
0x00, 0xcc, 0x0f, 0xf0, 0x30, 0x00, 0x03, 0x03,
0x03, 0x0c, 0x0c, 0x03, 0x00, 0x0c, 0x00, 0xc0,
0x0f, 0xfc, 0x00, 0xc0, 0x03, 0x03, 0x33, 0x0c,
0x0c, 0x30, 0x30, 0xc0, 0x03, 0x03, 0x0c, 0x00,
0x30, 0x00, 0xc0, 0x03, 0x03, 0x00, 0xc0, 0x00,
0x30, 0xcc, 0x03, 0x00, 0x0c, 0xcc, 0x3c, 0x30,
0xc0, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0xc0,
0x00, 0x30, 0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc0,
0xcc, 0x03, 0x30, 0x00, 0xc0, 0xf0, 0x00, 0xc0,
0x00, 0x3c, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x03,
0xf0, 0x3f, 0xc0, 0x3f, 0xc0, 0xff, 0x03, 0xf0,
0x0c, 0x00, 0x3f, 0x03, 0xfc, 0x03, 0xc0, 0x03,
0xc0, 0xc0, 0xc0, 0x30, 0x0f, 0x3c, 0x3f, 0xc0,
0x3f, 0x03, 0xfc, 0x03, 0xfc, 0x33, 0xf0, 0x3f,
0xc3, 0xfc, 0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc3,
0x03, 0x0c, 0x0c, 0x3f, 0xf0, 0x3c, 0x00, 0x30,
0x00, 0xf0, 0x00, 0x00, 0x33, 0x00, 0x30, 0x00,
0x00, 0x0c, 0xc0, 0x3f, 0x00, 0x30, 0x03, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x3f,
0xf0, 0x00, 0x03, 0xff, 0x00, 0x00, 0x03, 0x00,
0xcc, 0xc0, 0x30, 0x00, 0xf0, 0x03, 0xc0, 0xc3,
0x00, 0x03, 0x0f, 0xf0, 0x03, 0x00, 0x3f, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x00, 0x0c, 0x03, 0x00, 0xcf, 0xc3, 0x03, 0x0f,
0xf0, 0x30, 0x00, 0xc0, 0xc3, 0xfc, 0x0f, 0xf0,
0x30, 0x00, 0xff, 0xc0, 0x30, 0x00, 0x0c, 0x3c,
0x00, 0xc0, 0x03, 0x33, 0x0c, 0xcc, 0x30, 0x30,
0xff, 0x03, 0x03, 0x0f, 0xf0, 0x0f, 0xc0, 0x0c,
0x03, 0x03, 0x0c, 0x0c, 0x33, 0x30, 0x0c, 0x00,
0x30, 0x00, 0xc0, 0x3c, 0x00, 0x0c, 0x00, 0x0f,
0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c,
0x0c, 0x30, 0x00, 0xc0, 0xc3, 0x03, 0x0f, 0xf0,
0x30, 0x30, 0xc0, 0xc0, 0x30, 0x00, 0x30, 0x30,
0xc0, 0x0c, 0x03, 0x33, 0x0c, 0x0c, 0x30, 0x30,
0xc0, 0xc3, 0x03, 0x0f, 0x00, 0x30, 0x00, 0x30,
0x03, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0x33, 0x03,
0x03, 0x00, 0x30, 0x3c, 0x00, 0x0c, 0x00, 0x0f,
0x00, 0x00, 0x33, 0x30, 0x0c, 0x00, 0x00, 0x03,
0x30, 0x0f, 0xc0, 0x0c, 0x00, 0xc0, 0x00, 0x00,
0x30, 0x00, 0x00, 0xc0, 0x30, 0x0f, 0xfc, 0x00,
0x00, 0xff, 0xc0, 0x00, 0x00, 0xc0, 0x33, 0x30,
0x0c, 0x00, 0x3c, 0x00, 0xf0, 0x30, 0xc0, 0x00,
0xc3, 0xfc, 0x00, 0xc0, 0x0f, 0xc0, 0x3f, 0xc0,
0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03,
0x00, 0xc0, 0x33, 0xf0, 0xc0, 0xc3, 0xfc, 0x0c,
0x00, 0x30, 0x30, 0xff, 0x03, 0xfc, 0x0c, 0x00,
0x3f, 0xf0, 0x0c, 0x00, 0x03, 0x0f, 0x00, 0x30,
0x00, 0xcc, 0xc3, 0x33, 0x0c, 0x0c, 0x3f, 0xc0,
0xc0, 0xc3, 0xfc, 0x03, 0xf0, 0x03, 0x00, 0xc0,
0xc3, 0x03, 0x0c, 0xcc, 0x03, 0x00, 0x0c, 0x00,
0x30, 0x0f, 0x00, 0x03, 0x00, 0x03, 0xc0, 0xcc,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x03, 0x0c,
0x00, 0x30, 0x30, 0xc0, 0xc3, 0xfc, 0x0c, 0x0c,
0x30, 0x30, 0x0c, 0x00, 0x0c, 0x0c, 0x30, 0x03,
0x00, 0xcc, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30,
0xc0, 0xc3, 0xc0, 0x0c, 0x00, 0x0c, 0x00, 0xc0,
0xc3, 0x03, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0,
0x0c, 0x0f, 0x00, 0x03, 0x00, 0x03, 0xc0, 0x00,
0x0c, 0xcc, 0x03, 0x00, 0x00, 0x03, 0xff, 0x00,
0xcc, 0x0c, 0x00, 0xcc, 0xc0, 0x00, 0x0c, 0x00,
0x00, 0x30, 0x3f, 0x00, 0x30, 0x00, 0xc0, 0x00,
0x00, 0x00, 0x00, 0xc0, 0x0f, 0x0c, 0x03, 0x00,
0x30, 0x00, 0x03, 0x0f, 0xfc, 0x00, 0x30, 0xc0,
0xc0, 0xc0, 0x0c, 0x0c, 0x00, 0x30, 0x0c, 0x00,
0x30, 0x03, 0x00, 0x3f, 0xf0, 0x03, 0x00, 0x30,
0x0c, 0xf0, 0x3f, 0xf0, 0xc0, 0xc3, 0x00, 0x0c,
0x0c, 0x30, 0x00, 0xc0, 0x03, 0x0f, 0x0c, 0x0c,
0x03, 0x00, 0x00, 0xc3, 0x30, 0x0c, 0x00, 0x30,
0x30, 0xc3, 0xc3, 0x03, 0x0c, 0x00, 0x33, 0x30,
0xcc, 0x00, 0x03, 0x00, 0xc0, 0x30, 0x30, 0xc0,
0xc3, 0x33, 0x03, 0x30, 0x03, 0x00, 0x30, 0x03,
0xc0, 0x00, 0x30, 0x00, 0xf0, 0xc0, 0xc0, 0x00,
0x00, 0x00, 0x0f, 0xf0, 0xc0, 0xc3, 0x00, 0x0c,
0x0c, 0x3f, 0xf0, 0x30, 0x03, 0x03, 0x0c, 0x0c,
0x03, 0x00, 0x03, 0x03, 0xf0, 0x00, 0xc0, 0x33,
0x30, 0xc0, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x30,
0xc0, 0x00, 0xfc, 0x03, 0x00, 0x30, 0x30, 0xc0,
0xc3, 0x33, 0x00, 0xc0, 0x30, 0x30, 0x0c, 0x00,
0xf0, 0x00, 0xc0, 0x03, 0xc0, 0x00, 0x00, 0xcc,
0x00, 0xc0, 0x00, 0x00, 0xff, 0xc0, 0x33, 0x03,
0x00, 0x33, 0x30, 0x00, 0x03, 0x00, 0x00, 0x0c,
0x0f, 0xc0, 0x0c, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x30, 0x03, 0xc3, 0x00, 0xc0, 0x0c, 0x00,
0x00, 0xc3, 0xff, 0x00, 0x0c, 0x30, 0x30, 0x30,
0x03, 0x03, 0x00, 0x0c, 0x03, 0x00, 0x0c, 0x00,
0xc0, 0x0f, 0xfc, 0x00, 0xc0, 0x0c, 0x03, 0x3c,
0x0f, 0xfc, 0x30, 0x30, 0xc0, 0x03, 0x03, 0x0c,
0x00, 0x30, 0x00, 0xc3, 0xc3, 0x03, 0x00, 0xc0,
0x00, 0x30, 0xcc, 0x03, 0x00, 0x0c, 0x0c, 0x30,
0xf0, 0xc0, 0xc3, 0x00, 0x0c, 0xcc, 0x33, 0x00,
0x00, 0xc0, 0x30, 0x0c, 0x0c, 0x30, 0x30, 0xcc,
0xc0, 0xcc, 0x00, 0xc0, 0x0c, 0x00, 0xf0, 0x00,
0x0c, 0x00, 0x3c, 0x30, 0x30, 0x00, 0x00, 0x00,
0x03, 0xfc, 0x30, 0x30, 0xc0, 0x03, 0x03, 0x0f,
0xfc, 0x0c, 0x00, 0xc0, 0xc3, 0x03, 0x00, 0xc0,
0x00, 0xc0, 0xfc, 0x00, 0x30, 0x0c, 0xcc, 0x30,
0x30, 0xc0, 0xc3, 0x03, 0x0c, 0x0c, 0x30, 0x00,
0x3f, 0x00, 0xc0, 0x0c, 0x0c, 0x30, 0x30, 0xcc,
0xc0, 0x30, 0x0c, 0x0c, 0x03, 0x00, 0x3c, 0x00,
0x30, 0x00, 0xf0, 0x00, 0x00, 0x33, 0x00, 0x00,
0x00, 0x00, 0x0c, 0xc0, 0xff, 0x03, 0x0f, 0x0c,
0x30, 0x00, 0x00, 0x30, 0x00, 0x0c, 0x0c, 0xcc,
0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30,
0x00, 0xc0, 0xc0, 0x30, 0x0c, 0x00, 0x30, 0x30,
0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x00, 0xc0,
0xc0, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x00, 0xc0, 0x03, 0x03,
0x0c, 0x0c, 0x30, 0x30, 0xc0, 0xc3, 0x00, 0x0c,
0x00, 0x30, 0x30, 0xc0, 0xc0, 0x30, 0x0c, 0x0c,
0x30, 0xc0, 0xc0, 0x03, 0x03, 0x0c, 0x0c, 0x30,
0x30, 0xc0, 0x03, 0x0c, 0x0c, 0x30, 0x30, 0x30,
0x0c, 0x03, 0x03, 0x03, 0x30, 0x3c, 0xf0, 0xc0,
0xc0, 0x30, 0x0c, 0x00, 0x3c, 0x00, 0x00, 0xc0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
0x0c, 0x0c, 0x30, 0x00, 0xc0, 0xc3, 0x00, 0x03,
0x00, 0x0f, 0xf0, 0xc0, 0xc0, 0x30, 0x00, 0x30,
0x30, 0xc0, 0x0c, 0x03, 0x33, 0x0c, 0x0c, 0x30,
0x30, 0xff, 0x00, 0xff, 0x0c, 0x00, 0x00, 0x30,
0x30, 0xc3, 0x0f, 0x03, 0x30, 0x33, 0x30, 0x33,
0x00, 0xff, 0x03, 0x00, 0x0f, 0x00, 0x0c, 0x00,
0x3c, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00,
0x03, 0x30, 0x3f, 0xc0, 0xc3, 0xc3, 0x0c, 0x00,
0x00, 0x0c, 0x00, 0x03, 0x03, 0x33, 0x00, 0xc0,
0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x30,
0x30, 0x0c, 0x03, 0x00, 0x0c, 0x0c, 0x00, 0xc0,
0xc0, 0xc3, 0x03, 0x03, 0x00, 0x30, 0x30, 0x03,
0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x30, 0x00, 0xc0, 0xc3, 0x03,
0x0c, 0x0c, 0x30, 0x30, 0xc0, 0x03, 0x00, 0x0c,
0x0c, 0x30, 0x30, 0x0c, 0x03, 0x03, 0x0c, 0x30,
0x30, 0x00, 0xc0, 0xc3, 0x03, 0x0c, 0x0c, 0x30,
0x00, 0xc3, 0x03, 0x0c, 0x0c, 0x0c, 0x03, 0x00,
0xc0, 0xc0, 0xcc, 0x0f, 0x3c, 0x30, 0x30, 0x0c,
0x03, 0x00, 0x0f, 0x00, 0x00, 0x30, 0x03, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc3, 0x03,
0x0c, 0x00, 0x30, 0x30, 0xc0, 0x00, 0xc0, 0x03,
0xfc, 0x30, 0x30, 0x0c, 0x00, 0x0c, 0x0c, 0x30,
0x03, 0x00, 0xcc, 0xc3, 0x03, 0x0c, 0x0c, 0x3f,
0xc0, 0x3f, 0xc3, 0x00, 0x00, 0x0c, 0x0c, 0x30,
0xc3, 0xc0, 0xcc, 0x0c, 0xcc, 0x0c, 0xc0, 0x3f,
0xc0, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x0f, 0x00,
0x00, 0x0c, 0xcc, 0x03, 0x00, 0x00, 0x00, 0xcc,
0x00, 0xc0, 0x00, 0xf0, 0x3c, 0xc0, 0x00, 0x00,
0xc0, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x0c, 0x00, 0x00, 0x03, 0xf0, 0x0f,
0xc0, 0xff, 0xc0, 0xfc, 0x00, 0x30, 0x0f, 0xc0,
0x3f, 0x00, 0xc0, 0x03, 0xf0, 0x3f, 0x00, 0x00,
0x00, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00,
0x30, 0x03, 0xfc, 0x30, 0x30, 0xff, 0x00, 0xfc,
0x0f, 0xf0, 0x3f, 0xf0, 0xc0, 0x00, 0xff, 0x0c,
0x0c, 0x0f, 0xc0, 0x3f, 0x03, 0x03, 0x0f, 0xfc,
0x30, 0x30, 0xc0, 0xc0, 0xfc, 0x0c, 0x00, 0x0f,
0x30, 0xc0, 0xc0, 0xfc, 0x00, 0xc0, 0x0f, 0xc0,
0x0c, 0x03, 0x03, 0x0c, 0x0c, 0x03, 0x00, 0xff,
0xc3, 0xff, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0xff,
0x03, 0xfc, 0x0f, 0xf0, 0x30, 0x00, 0x03, 0x0c,
0x0c, 0x0f, 0xc0, 0xc3, 0x03, 0x03, 0x03, 0xf0,
0x30, 0x30, 0xc0, 0xc0, 0xfc, 0x0c, 0x00, 0x00,
0x30, 0xc0, 0x03, 0xfc, 0x00, 0xf0, 0x0f, 0x30,
0x0c, 0x03, 0xcf, 0x0c, 0x0c, 0x00, 0x30, 0xff,
0xc0, 0x3f, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x00, 0x33, 0x00, 0x30,
0x00, 0x3c, 0x0f, 0x30, 0x00, 0x00, 0x30, 0x00,
0xc0, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xf0, 0x3f,
0xf0, 0x3f, 0x00, 0x0c, 0x03, 0xf0, 0x0f, 0xc0,
0x30, 0x00, 0xfc, 0x0f, 0xc0, 0x00, 0x00, 0x30,
0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00,
0xff, 0x0c, 0x0c, 0x3f, 0xc0, 0x3f, 0x03, 0xfc,
0x0f, 0xfc, 0x30, 0x00, 0x3f, 0xc3, 0x03, 0x03,
0xf0, 0x0f, 0xc0, 0xc0, 0xc3, 0xff, 0x0c, 0x0c,
0x30, 0x30, 0x3f, 0x03, 0x00, 0x03, 0xcc, 0x30,
0x30, 0x3f, 0x00, 0x30, 0x03, 0xf0, 0x03, 0x00,
0xc0, 0xc3, 0x03, 0x00, 0xc0, 0x3f, 0xf0, 0xff,
0xc0, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xfc, 0x3f, 0xc0, 0x3f, 0xc0, 0xff,
0x03, 0xfc, 0x0c, 0x00, 0x00, 0xc3, 0x03, 0x03,
0xf0, 0x30, 0xc0, 0xc0, 0xc0, 0xfc, 0x0c, 0x0c,
0x30, 0x30, 0x3f, 0x03, 0x00, 0x00, 0x0c, 0x30,
0x00, 0xff, 0x00, 0x3c, 0x03, 0xcc, 0x03, 0x00,
0xf3, 0xc3, 0x03, 0x00, 0x0c, 0x3f, 0xf0, 0x0f,
0xc0, 0x30, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x03,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
}
};
/* File: gpio_extra.c
* ------------------
* Support for GPIO event detection and internal pull-up/down
*
* Author: Julie Zelenski <[email protected]>
*
* Last modified: Jan 2018
*/
#include "gpio.h"
#include "gpio_extra.h"
#include "_gpio_private.h"
#include <stddef.h>
/*
* See BCM p 90 for layout of gpio peripheral registers
Reminder of names/order
uint32_t eds[2]; // event detect status
uint32_t ren[2]; // rising edge detect enable
uint32_t fen[2]; // falling edge detect enable
uint32_t hen[2]; // high detect enable
uint32_t len[2]; // low detect enable
uint32_t aren[2]; // async rising edge detect enable
uint32_t afen[2]; // async falling edge detect enable
uint32_t pud; // pull-up/down signal
uint32_t pudclk[2]; // pull-up/down clock
*/
/*
Reminder from .h, this is the order the registers need to be placed in register array
enum gpio_event {
GPIO_DETECT_RISING_EDGE = 0,
GPIO_DETECT_FALLING_EDGE,
GPIO_DETECT_HIGH_LEVEL,
GPIO_DETECT_LOW_LEVEL,
GPIO_DETECT_ASYNC_RISING_EDGE,
GPIO_DETECT_ASYNC_FALLING_EDGE,
};
*/
static volatile uint32_t *gpio_reg_for_event(unsigned int event) {
volatile uint32_t *registers[] = { _gpio->ren, _gpio->fen, _gpio->hen, _gpio->len, _gpio->aren, _gpio->afen};
if (event < 0 || event >= sizeof(registers) / sizeof(*registers)) {
return NULL;
}
return registers[event];
}
static void gpio_set_detection(unsigned int pin, unsigned int event, unsigned int enabled) {
volatile uint32_t *reg = gpio_reg_for_event(event);
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST || reg == NULL) {
return;
}
unsigned int bank = pin / 32;
unsigned int shift = pin % 32;
if (enabled) {
reg[bank] |= 1 << shift;
} else {
reg[bank] &= ~(1 << shift);
}
gpio_clear_event(pin);
}
bool gpio_get_event_detection(unsigned int pin, unsigned int event) {
volatile uint32_t *reg = gpio_reg_for_event(event);
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST || reg == NULL) {
return false;
}
unsigned int bank = pin / 32;
unsigned int shift = pin % 32;
return (reg[bank] & (1 << shift)) != 0;
}
void gpio_disable_event_detection(unsigned int pin, unsigned int event) {
gpio_set_detection(pin, event, 0);
}
void gpio_enable_event_detection(unsigned int pin, unsigned int event) {
gpio_set_detection(pin, event, 1);
}
bool gpio_check_event(unsigned int pin) {
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) {
return false;
}
unsigned int bank = pin / 32;
unsigned int shift = pin % 32;
return (_gpio->eds[bank] & (1 << shift)) != 0;
}
void gpio_clear_event(unsigned int pin) {
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) {
return;
}
unsigned int bank = pin / 32;
unsigned int shift = pin % 32;
if (gpio_check_event(pin)) {
_gpio->eds[bank] = 1 << shift;
}
}
bool gpio_check_and_clear_event(unsigned int pin) {
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) {
return false;
}
bool event = gpio_check_event(pin);
if (event) {
gpio_clear_event(pin);
}
return event;
}
void gpio_disable_all_event_detection(unsigned int pin) {
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) {
return;
}
for (int i = GPIO_DETECT_RISING_EDGE; i <= GPIO_DETECT_ASYNC_FALLING_EDGE; i++) {
gpio_set_detection(pin, i, 0);
}
gpio_clear_event(pin);
}
/*
* From Broadcom manual about how to use the pull-up/down:
* -------------------------------------------------------
* The GPIO Pull-up/down Clock Registers control the actuation of internal
* pull-downs on the respective GPIO pins. These registers must be used
* in conjunction with the GPPUD register to effect GPIO Pull-up/down changes.
* The following sequence of events is required:
* 1. Write to GPPUD to set the required control signal
* (i.e. Pull-up or Pull-Down or neither to remove the current Pull-up/down)
* 2. Wait 150 cycles – this provides the required set-up time for the control signal
* 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish
* to modify – NOTE only the pads which receive a clock will be modified,
* all others will retain their previous state.
* 4. Wait 150 cycles – this provides the required hold time for the control signal
* 5. Write to GPPUD to remove the control signal
* 6. Write to GPPUDCLK0/1 to remove the clock
*/
enum { GPIO_PUD_DISABLE = 0, GPIO_PUD_PULLDOWN = 1, GPIO_PUD_PULLUP = 2 };
static void gpio_set_pud(unsigned int pin, unsigned int pud) {
if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) {
return;
}
unsigned int bank = pin / 32;
unsigned int shift = pin % 32;
_gpio->pud = pud;
for (volatile int i = 0; i < 150; i++) ;
_gpio->pudclk[bank] = 1 << shift;
for (volatile int i = 0; i < 150; i++) ;
_gpio->pudclk[bank] = 0;
}
void gpio_set_pullup(unsigned int pin) {
gpio_set_pud(pin, GPIO_PUD_PULLUP);
}
void gpio_set_pulldown(unsigned int pin) {
gpio_set_pud(pin, GPIO_PUD_PULLDOWN);
}
void gpio_set_pullnone(unsigned int pin) {
gpio_set_pud(pin, GPIO_PUD_DISABLE);
}
/* File: gpio_interrupts.c
* -----------------------
* Dispatcher for GPIO interrupts.
*
* Author: Philip Levis <[email protected]>
* Julie Zelenski <[email protected]>
*
* Last modified: Feb 2022
*/
#include "assert.h"
#include "bits.h"
#include "gpio_interrupts.h"
#include "gpio.h"
#include "gpio_extra.h"
#include "_gpio_private.h"
#include "interrupts.h"
#include <stddef.h>
#include <stdbool.h>
static bool gpio_interrupts_initialized = false;
// This array stores the handlers for gpio interrupts, one per pin.
// If no handler is registered for a given pin, fn at that index is NULL
// global array in bss, values zero'ed at program start
static struct {
handler_fn_t fn;
void *aux_data;
} handlers[GPIO_PIN_LAST + 1];
static void gpio_interrupt_dispatch(unsigned int, void *);
void gpio_interrupts_init(void) {
// okay to re-init this module
// will not wipe per-pin handlers that were previously registered
// re-register gpio dispatch as handler with top-level interrupts modules
// reset gpio interrupt source to disabled state
interrupts_disable_source(INTERRUPTS_GPIO3);
interrupts_register_handler(INTERRUPTS_GPIO3, gpio_interrupt_dispatch, NULL);
gpio_interrupts_initialized = true;
}
void gpio_interrupts_enable(void) {
assert(gpio_interrupts_initialized);
interrupts_enable_source(INTERRUPTS_GPIO3);
}
void gpio_interrupts_disable(void) {
assert(gpio_interrupts_initialized);
interrupts_disable_source(INTERRUPTS_GPIO3);
}
void gpio_interrupts_register_handler(unsigned int pin, handler_fn_t fn, void *aux_data) {
assert(gpio_interrupts_initialized);
assert(pin <= GPIO_PIN_LAST);
handlers[pin].fn = fn;
handlers[pin].aux_data = aux_data;
}
static unsigned int get_next_pin(void) {
unsigned int bank0_zeroes = count_leading_zeroes(_gpio->eds[0]);
unsigned int bank1_zeroes = count_leading_zeroes(_gpio->eds[1]);
if (bank0_zeroes != 32) {
return 31 - bank0_zeroes;
} else if (bank1_zeroes != 32) {
return 63 - bank1_zeroes;
} else {
return GPIO_PIN_LAST + 1;
}
}
// gpio_interrupt_dispatch is registered with top-level interrupts
// module as handler for source INTERRUPTS_GPIO3. This handler is
// thus called for all GPIO interrupts and then performs second-level
// dispatch to the per-pin handlers that have been registered with
// this module (gpio_interrupts)
static void gpio_interrupt_dispatch(unsigned int pc, void *unused) {
unsigned int pin = get_next_pin();
if (pin <= GPIO_PIN_LAST && handlers[pin].fn != NULL) {
handlers[pin].fn(pc, handlers[pin].aux_data);
}
}
/*
* Module to configure/enable/dispatch interrupts at top-level
*
* Author: Philip Levis <[email protected]>
* Julie Zelenski <[email protected]>
*
* Last modified: Feb 2022
*/
#include "assert.h"
#include "bits.h"
#include "interrupts.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// http://xinu.mscs.mu.edu/BCM2835_Interrupt_Controller
// BCM: p196
// This struct is declared to match memory layout of the interrupt registers
struct interrupt_t {
uint32_t pending_basic;
uint32_t pending[2];
uint32_t fiq_control;
uint32_t enable[2];
uint32_t enable_basic;
uint32_t disable[2];
uint32_t disable_basic;
};
#define INTERRUPT_CONTROLLER_BASE (void *)0x2000B200
static volatile struct interrupt_t * const interrupt = INTERRUPT_CONTROLLER_BASE;
static const uint64_t irq_safe_mask = (1ULL << INTERRUPTS_AUX) |
(1ULL << INTERRUPTS_I2CSPISLV) |
(1ULL << INTERRUPTS_PWA0) |
(1ULL << INTERRUPTS_PWA1) |
(1ULL << INTERRUPTS_CPR) |
(1ULL << INTERRUPTS_SMI) |
(1ULL << INTERRUPTS_GPIO0) |
(1ULL << INTERRUPTS_GPIO1) |
(1ULL << INTERRUPTS_GPIO2) |
(1ULL << INTERRUPTS_GPIO3) |
(1ULL << INTERRUPTS_VC_I2C) |
(1ULL << INTERRUPTS_VC_SPI) |
(1ULL << INTERRUPTS_VC_I2SPCM) |
(1ULL << INTERRUPTS_VC_UART);
extern uint32_t _vectors, _vectors_end;
extern uint32_t *_RPI_INTERRUPT_VECTOR_BASE;
// This array stores the handlers for peripheral interrupts (IRQs) and as well
// as basic interrupts. The 2835 has 64 IRQs, these are entries 0-63. We
// place the basic interrupts after them (see the enumeration in
// interrupts.h). If no handler is registered for a given interrupt source,
// the fn at that index is NULL.
static struct {
handler_fn_t fn;
void *aux_data;
} handlers[INTERRUPTS_COUNT];
static bool interrupts_initialized = false;
void interrupts_init(void) {
// init should happen once and only once
// A re-init would disable all previously enabled sources
// raise error rather than do that.
assert(!interrupts_initialized);
// disable interrupt generation system-wide
interrupts_global_disable();
// disable all sources
interrupt->disable_basic = 0xffffffff;
interrupt->disable[0] = 0xffffffff;
interrupt->disable[1] = 0xffffffff;
// copy table of vectors to destination RPI_INTERRUPT_VECTOR_BASE
// _vector and _vector_end are symbols defined in interrupt_asm.s
// that mark start/end of table to be copied
uint32_t *dst = _RPI_INTERRUPT_VECTOR_BASE;
uint32_t *src = &_vectors;
uint32_t n = &_vectors_end - &_vectors;
for (int i = 0; i < n; i++) {
dst[i] = src[i];
}
interrupts_initialized = true;
}
// verify vector table correctly installed, i.e. interrupts_init() was
// called and IRQ table entry is not corrupted
static bool vector_table_is_installed(void) {
const int IRQ_INDEX = 6;
return _RPI_INTERRUPT_VECTOR_BASE[IRQ_INDEX] == (&_vectors)[IRQ_INDEX];
}
// basic IRQ sources are ARM-specific
static bool is_basic(unsigned int irq_source) {
return irq_source >= INTERRUPTS_BASIC_BASE && irq_source < INTERRUPTS_BASIC_END;
}
// these IRQ sources are shared between CPU and GPU
static bool is_shared(unsigned int irq_source) {
return irq_source >= INTERRUPTS_SHARED_START && irq_source < INTERRUPTS_SHARED_END;
}
// Returns whether a given IRQ is safe to be used.
// From the BCM2835 manual 7.5: "the table above has many empty entries.
// These should not be enabled as they will interfere with the GPU operation."
// Returns whether the irq is a non-empty entry, and thus safe to be enabled.
static bool is_safe(unsigned int irq_source) {
uint64_t bit = 1ULL << irq_source;
return ((bit & irq_safe_mask) != 0);
}
static bool is_valid(unsigned int irq_source) {
return is_basic(irq_source) || (is_shared(irq_source) && is_safe(irq_source));
}
void interrupts_enable_source(unsigned int source) {
assert(interrupts_initialized);
assert(is_valid(source));
if (is_basic(source)) {
unsigned int shift = source - INTERRUPTS_BASIC_BASE;
interrupt->enable_basic |= 1 << shift;
} else if (is_shared(source)) {
unsigned int bank = source / 32;
unsigned int shift = source % 32;
interrupt->enable[bank] |= 1 << shift;
}
}
void interrupts_disable_source(unsigned int source) {
assert(interrupts_initialized);
assert(is_valid(source));
if (is_basic(source)) {
unsigned int shift = source - INTERRUPTS_BASIC_BASE;
interrupt->disable_basic |= 1 << shift;
} else if (is_shared(source)) {
unsigned int bank = source / 32;
unsigned int shift = source % 32;
interrupt->disable[bank] |= 1 << shift;
}
}
void interrupts_register_handler(unsigned int source, handler_fn_t fn, void *aux_data) {
assert(interrupts_initialized);
assert(vector_table_is_installed());
assert(is_valid(source));
handlers[source].fn = fn;
handlers[source].aux_data = aux_data;
}
static int get_next_source(void) {
unsigned int basic_zeroes = count_leading_zeroes(interrupt->pending_basic);
unsigned int pending0_zeroes = count_leading_zeroes(interrupt->pending[0]);
unsigned int pending1_zeroes = count_leading_zeroes(interrupt->pending[1]);
// We only care about basic bits 0-7:
// - bits 8 and 9 are for the pending registers, which we always check
// - higher bits are GPU which we ignore
if (basic_zeroes >= 24) {
// If bit 0 is set there will be 31 leading zeroes, while if bit 31 is set
// there are 0 leading zeroes. So the number is 31 - number of zeroes; add
// INTERRUPTS_BASIC_BASE for index into table.
return (31 - basic_zeroes) + INTERRUPTS_BASIC_BASE;
} else if (pending0_zeroes != 32) {
return (31 - pending0_zeroes);
} else if (pending1_zeroes != 32) {
return (63 - pending1_zeroes);
} else {
return INTERRUPTS_NONE;
}
}
// The dispatch function must be extern as it is called from another module
// (interrupts_asm.s). However, it not otherwise intended as a public
// function and is not declared or documented in the interface.
void interrupt_dispatch(unsigned int pc);
void interrupt_dispatch(unsigned int pc) {
int source = get_next_source();
if (source < INTERRUPTS_COUNT && handlers[source].fn != NULL) {
handlers[source].fn(pc, handlers[source].aux_data);
}
}
/*
* Assembly code for interrupt vector table and functions
* to enable/disable IRQ interrupts on the Rapsberry Pi in CS107E.
*
* Author: Philip Levis <[email protected]>
* Julie Zelenski <[email protected]>
*
* Last update: 2/20/20
*
* Enable/disable interrupts.
*
* CPSR = current program status register
* lower 8 bits are:
* 7 6 5 4 3 2 1 0
* +-+-+-+---------+
* |I|F|T| Mode |
* +-+-+-+---------+
*
* I : = 0 IRQ enabled, = 1 IRQ disabled
* F : = 0 FIQ enabled, = 1 FIQ disabled
* T : = 0 indicates ARM execution, = 1 is thumb execution
* Mode : current mode
*/
.global interrupts_global_enable
interrupts_global_enable:
mrs r0, cpsr // copy cpsr value to regular register
bic r0, r0, #0x80 // clear I=0 enables IRQ interrupts
msr cpsr_c, r0 // copy back, control bits only
bx lr
.global interrupts_global_disable
interrupts_global_disable:
mrs r0, cpsr // copy cpsr value to regular register
orr r0, r0, #0x80 // set I=1 disables IRQ interrupts
msr cpsr_c, r0 // copy back, control bits only
bx lr
// destination address for vector table (0)
.global _RPI_INTERRUPT_VECTOR_BASE
_RPI_INTERRUPT_VECTOR_BASE: .word 0
.global _vectors
.global _vectors_end
// Vector table has one instruction for each of the eight exceptions
// Bounces to absolute destination address accessed via pc-relative label
_vectors:
ldr pc, abort_addr
ldr pc, abort_addr
ldr pc, abort_addr
ldr pc, abort_addr
ldr pc, abort_addr
ldr pc, abort_addr
ldr pc, interrupt_addr
ldr pc, abort_addr
abort_addr: .word abort_asm
interrupt_addr: .word interrupt_asm
_vectors_end:
// Used for fatal exceptions
abort_asm:
mov sp, #0x4000 // init stack
bl pi_abort // pi_abort does not return
// Used for general interrupts
interrupt_asm:
mov sp, #0x8000 // init stack for interrupt mode
sub lr, lr, #4 // compute resume addr from old pc
push {r0-r12, lr} // save all registers (overkill, but simple & correct)
mov r0, lr // pass resume addr as argument to dispatch
bl interrupt_dispatch // call C function
ldm sp!, {r0-r12, pc}^ // leave interrupt mode
// restore saved regs, pc restored to lr (resume addr)
// The ^ changes to previous mode, restores cpsr
/*
* Interface to Raspberry Pi mailbox system, used by libpi to interact
* with GPU. Note that this interface has been deprecated and so future
* code should use the property interface instead.
*
* Author: Pat Hanrahan <[email protected]>
* Philip Levis <[email protected]>
* Date: 6/20/17
*/
#include "mailbox.h"
#define MAILBOX_BASE 0x2000B880
// This bit is set in status register if no space to write to mailbox
#define MAILBOX_FULL (1<<31)
// This bit is set in status register if nothing to read in mailbox
#define MAILBOX_EMPTY (1<<30)
// This prevents the GPU and CPU from caching mailbox messages
#define GPU_NOCACHE 0x40000000
typedef struct {
unsigned int read;
unsigned int padding[3]; // note padding to skip 3 words
unsigned int peek;
unsigned int sender;
unsigned int status;
unsigned int configuration;
unsigned int write;
} mailbox_t;
// mailbox is volatile because the GPU can also write to it
static volatile mailbox_t *mailbox = (volatile mailbox_t *)MAILBOX_BASE;
bool mailbox_request(unsigned int channel, unsigned int addr) {
if (!mailbox_write(channel, addr))
return false;
return (mailbox_read(channel) == 0); // returned data is 0 on success
}
bool mailbox_write(unsigned int channel, unsigned int addr) {
// mailbox has a maximum of 16 channels
if (channel >= MAILBOX_MAXCHANNEL) {
return false;
}
// addr must be a multiple of 16
if (addr & 0xF) {
return false;
}
// wait until mailbox is not full ...
while (mailbox->status & MAILBOX_FULL) ;
// set GPU_NOCACHE bit so that the GPU does not cache the memory
addr |= GPU_NOCACHE;
// addr is a multiple of 16, so the low 4 bits are zeros
// 4-bit channel number is stuffed into those low bits
mailbox->write = addr | channel;
return true;
}
unsigned int mailbox_read(unsigned int channel) {
if (channel >= MAILBOX_MAXCHANNEL) {
return 1;
}
while (1) {
// wait until mailbox is not empty ...
while (mailbox->status & MAILBOX_EMPTY) ;
// read the data, low 4 bits are channel, upper 28 are result
unsigned int data = mailbox->read;
if ((data & 0xF) == channel) { // if this message is for our channel
return (data >> 4);
}
}
return 1;
}
/* File: pi.c
* ----------
* Utility functions for Raspberry Pi.
* (reboot/abort execution, control on-board LEDs)
*
* Author: Pat Hanrahan <[email protected]>
* Julie Zelenski <[email protected]>
*
* Last modified: Jan 2018
*/
#include "pi.h"
#include <stdint.h>
#include "timer.h"
#include "uart.h"
void pi_reboot(void) {
static const int PM_RSTC = 0x2010001c;
static const int PM_WDOG = 0x20100024;
static const int PM_PASSWORD = 0x5a000000;
static const int PM_RSTC_WRCFG_FULL_RESET = 0x00000020;
volatile uint32_t * reg_pm_wdog = (uint32_t *)PM_WDOG;
volatile uint32_t * reg_pm_rstc = (uint32_t *)PM_RSTC;
uart_putchar(EOT);
// give output time to flush.
for (int i = 0; i < 100000; i++) {
__asm__("nop");
}
// timeout = 1/16th of a second? (whatever)
*reg_pm_wdog = PM_PASSWORD | 1;
*reg_pm_rstc = PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
while (1)
;
}
void pi_abort(void) {
gpio_set_output(PI_PWR_LED);
while (1) { // Infinite loop, flash the red PWR LED
pi_led_toggle(PI_PWR_LED);
timer_delay_ms(200);
}
}
void pi_led_on(int led) {
if (led != PI_ACT_LED && led != PI_PWR_LED) {
return;
}
gpio_set_output(led);
gpio_write(led, 1);
}
void pi_led_off(int led) {
if (led != PI_ACT_LED && led != PI_PWR_LED) {
return;
}
gpio_set_output(led);
gpio_write(led, 0);
}
void pi_led_toggle(int led) {
if (led != PI_ACT_LED && led != PI_PWR_LED) {
return;
}
gpio_set_output(led);
gpio_write(led, !gpio_read(led));
}
/*
* Lookup table to access key information for a PS/2 scan code.
*
* Author: Julie Zelenski <[email protected]>
*/
#include "ps2_keys.h"
#define UNUSED { PS2_KEY_NONE, PS2_KEY_NONE }
// For completeness, array lists all keys on the full PS/2 keyboard.
// Refer to the assign5 writeup for spec on which keys are required
// to be implemented by your keyboard driver.
ps2_key_t const ps2_keys[] = {
/* scan code */
/* 00 */ UNUSED,
/* 01 */ { PS2_KEY_F9, PS2_KEY_F9 },
/* 02 */ UNUSED,
/* 03 */ { PS2_KEY_F5, PS2_KEY_F5 },
/* 04 */ { PS2_KEY_F3, PS2_KEY_F3 },
/* 05 */ { PS2_KEY_F1, PS2_KEY_F1 },
/* 06 */ { PS2_KEY_F2, PS2_KEY_F2 },
/* 07 */ { PS2_KEY_F12, PS2_KEY_F12 },
/* 08 */ UNUSED,
/* 09 */ { PS2_KEY_F10, PS2_KEY_F10 },
/* 0A */ { PS2_KEY_F8, PS2_KEY_F8 },
/* 0B */ { PS2_KEY_F6, PS2_KEY_F6 },
/* 0C */ { PS2_KEY_F4, PS2_KEY_F4 },
/* 0D */ { '\t', '\t' },
/* 0E */ { '`', '~' },
/* 0F */ UNUSED,
/* 10 */ UNUSED,
/* 11 */ { PS2_KEY_ALT, PS2_KEY_ALT },
/* 12 */ { PS2_KEY_SHIFT, PS2_KEY_SHIFT },
/* 13 */ UNUSED,
/* 14 */ { PS2_KEY_CTRL, PS2_KEY_CTRL },
/* 15 */ { 'q', 'Q' },
/* 16 */ { '1', '!' },
/* 17 */ UNUSED,
/* 18 */ UNUSED,
/* 19 */ UNUSED,
/* 1A */ { 'z', 'Z' },
/* 1B */ { 's', 'S' },
/* 1C */ { 'a', 'A' },
/* 1D */ { 'w', 'W' },
/* 1E */ { '2', '@' },
/* 1F */ UNUSED,
/* 20 */ UNUSED,
/* 21 */ { 'c', 'C' },
/* 22 */ { 'x', 'X' },
/* 23 */ { 'd', 'D' },
/* 24 */ { 'e', 'E' },
/* 25 */ { '4', '$' },
/* 26 */ { '3', '#' },
/* 27 */ UNUSED,
/* 28 */ UNUSED,
/* 29 */ { ' ', ' ' },
/* 2A */ { 'v', 'V' },
/* 2B */ { 'f', 'F' },
/* 2C */ { 't', 'T' },
/* 2D */ { 'r', 'R' },
/* 2E */ { '5', '%' },
/* 2F */ UNUSED,
/* 30 */ UNUSED,
/* 31 */ { 'n', 'N' },
/* 32 */ { 'b', 'B' },
/* 33 */ { 'h', 'H' },
/* 34 */ { 'g', 'G' },
/* 35 */ { 'y', 'Y' },
/* 36 */ { '6', '^' },
/* 37 */ UNUSED,
/* 38 */ UNUSED,
/* 39 */ UNUSED,
/* 3A */ { 'm', 'M' },
/* 3B */ { 'j', 'J' },
/* 3C */ { 'u', 'U' },
/* 3D */ { '7', '&' },
/* 3E */ { '8', '*' },
/* 3F */ UNUSED,
/* 40 */ UNUSED,
/* 41 */ { ',', '<' },
/* 42 */ { 'k', 'K' },
/* 43 */ { 'i', 'I' },
/* 44 */ { 'o', 'O' },
/* 45 */ { '0', ')' },
/* 46 */ { '9', '(' },
/* 47 */ UNUSED,
/* 48 */ UNUSED,
/* 49 */ { '.', '>' },
/* 4A */ { '/', '?' },
/* 4B */ { 'l', 'L' },
/* 4C */ { ';', ':' },
/* 4D */ { 'p', 'P' },
/* 4E */ { '-', '_' },
/* 4F */ UNUSED,
/* 50 */ UNUSED,
/* 51 */ UNUSED,
/* 52 */ { '\'', '"' },
/* 53 */ UNUSED,
/* 54 */ { '[', '{' },
/* 55 */ { '=', '+' },
/* 56 */ UNUSED,
/* 57 */ UNUSED,
/* 58 */ { PS2_KEY_CAPS_LOCK, PS2_KEY_CAPS_LOCK },
/* 59 */ { PS2_KEY_SHIFT, PS2_KEY_SHIFT },
/* 5A */ { '\n', '\n' },
/* 5B */ { ']', '}' },
/* 5C */ UNUSED,
/* 5D */ { '\\', '|' },
/* 5E */ UNUSED,
/* 5F */ UNUSED,
/* 60 */ UNUSED,
/* 61 */ UNUSED,
/* 62 */ UNUSED,
/* 63 */ UNUSED,
/* 64 */ UNUSED,
/* 65 */ UNUSED,
/* 66 */ { '\b', '\b' }, // delete key produces backspace (= ascii 0x08 = \b)
/* 67 */ UNUSED,
/* 68 */ UNUSED,
/* 69 */ { PS2_KEY_END, '1' },
/* 6A */ UNUSED,
/* 6B */ { PS2_KEY_ARROW_LEFT, '4' },
/* 6C */ { PS2_KEY_HOME, '7' },
/* 6D */ UNUSED,
/* 6E */ UNUSED,
/* 6F */ UNUSED,
/* 70 */ { PS2_KEY_INSERT, '0' },
/* 71 */ { PS2_KEY_DELETE, '.' },
/* 72 */ { PS2_KEY_ARROW_DOWN, '2' },
/* 73 */ { '5', '5' },
/* 74 */ { PS2_KEY_ARROW_RIGHT, '6' },
/* 75 */ { PS2_KEY_ARROW_UP, '8' },
/* 76 */ { PS2_KEY_ESC, PS2_KEY_ESC },
/* 77 */ { PS2_KEY_NUM_LOCK, PS2_KEY_NUM_LOCK },
/* 78 */ { PS2_KEY_F11, PS2_KEY_F11 },
/* 79 */ { '+', '+' },
/* 7A */ { PS2_KEY_PAGE_DOWN, '3' },
/* 7B */ { '-', '-' },
/* 7C */ { '*', '*' },
/* 7D */ { PS2_KEY_PAGE_UP, '9' },
/* 7E */ { PS2_KEY_SCROLL_LOCK, 0 },
/* 7F */ UNUSED,
/* 80 */ UNUSED,
/* 81 */ UNUSED,
/* 82 */ UNUSED,
/* 83 */ { PS2_KEY_F7, PS2_KEY_F7 },
};
/* File: ringbuffer.c
* ------------------
* Simple lock-free ring buffer that allows for concurrent
* access by 1 reader and 1 writer.
*
* Author: Philip Levis <[email protected]>
* Julie Zelenski <[email protected]>
*/
#include "ringbuffer.h"
#include "assert.h"
#include "malloc.h"
#define LENGTH 512
/*
* A ring buffer is represented using a struct containing a fixed-size array
* and head and tail fields, which are indexes into the entries[] array.
* head is the index of the frontmost element (head advances during dequeue)
* tail is the index of the next position to use (tail advances during enqueue)
* Both head and tail advance circularly, i.e. index = (index + 1) % LENGTH
* The ring buffer is empty if tail == head
* The ring buffer is full if tail + 1 == head
* (Note: one slot remains permanently empty to distinguish full from empty)
*/
struct ringbuffer {
int entries[LENGTH];
unsigned int head, tail;
};
rb_t *rb_new(void) {
rb_t *rb = malloc(sizeof(struct ringbuffer));
rb->head = rb->tail = 0;
return rb;
}
bool rb_empty(rb_t *rb) {
assert(rb != NULL);
return rb->head == rb->tail;
}
bool rb_full(rb_t *rb) {
assert(rb != NULL);
return (rb->tail + 1) % LENGTH == rb->head;
}
/*
* Note: enqueue is called by writer. enqueue advances rb->tail,
* no changes to rb->head. This design allows safe concurrent access.
*/
bool rb_enqueue(rb_t *rb, int elem) {
assert(rb != NULL);
if (rb_full(rb)) {
return false;
}
rb->entries[rb->tail] = elem;
rb->tail = (rb->tail + 1) % LENGTH;
return true;
}
/*
* Note: dequeue is called by reader. dequeue advances rb->head,
* no changes to rb->tail. This design allows safe concurrent access.
*/
bool rb_dequeue(rb_t *rb, int *p_elem) {
assert(rb != NULL && p_elem != NULL);
if (rb_empty(rb)) {
return false;
}
*p_elem = rb->entries[rb->head];
rb->head = (rb->head + 1) % LENGTH;
return true;
}
/*
* Hardware abstractions for a serial port (UART).
*
* Author: Pat Hanrahan <[email protected]>
* Date: Jan 24, 2016
*/
#include "gpio.h"
#include <stdint.h>
#include "uart.h"
struct UART {
uint32_t data; // I/O Data
uint32_t ier; // Interrupt enable
uint32_t iir; // Interrupt identify and fifo enables/clears
uint32_t lcr; // line control register
uint32_t mcr; // modem control register
uint32_t lsr; // line status register
uint32_t msr; // modem status register
uint32_t scratch;
uint32_t cntl; // control register
uint32_t stat; // status register
uint32_t baud; // baud rate register
} ;
// AUX bits
#define AUX_ENABLES 0x20215004
#define AUX_ENABLE 0x00000001
// Mini UART
#define MINI_UART_BASE 0x20215040
#define MINI_UART_IIR_RX_FIFO_CLEAR 0x00000002
#define MINI_UART_IIR_TX_FIFO_CLEAR 0x00000004
#define MINI_UART_IIR_RX_FIFO_ENABLE 0x00000080
#define MINI_UART_IIR_TX_FIFO_ENABLE 0x00000040
#define MINI_UART_LCR_8BIT 0x00000003
#define MINI_UART_LSR_RX_READY 0x00000001
#define MINI_UART_LSR_TX_READY 0x00000010
#define MINI_UART_LSR_TX_EMPTY 0x00000020
#define MINI_UART_CNTL_TX_ENABLE 0x00000002
#define MINI_UART_CNTL_RX_ENABLE 0x00000001
static volatile struct UART *uart = (struct UART*) MINI_UART_BASE;
static bool initialized = false;
/* Key detail from the Broadcom Peripherals data sheet p.10
*
* GPIO pins should be set up first the before enabling the UART.
* The UART core is build to emulate 16550 behaviour.
* So when it is enabled any data at the inputs will immediately be received .
* If the UART1_RX line is low (because the GPIO pins have not been set-up yet)
* that will be seen as a start bit and the UART will start receiving 0x00-characters.
* [...] The result will be that the FIFO is full and overflowing in no time flat.
*/
void uart_init(void) {
gpio_set_function(GPIO_TX, GPIO_FUNC_ALT5);
gpio_set_function(GPIO_RX, GPIO_FUNC_ALT5);
int *aux = (int*)AUX_ENABLES;
*aux |= AUX_ENABLE; // must enable mini-uart before accessing registers
uart->ier = 0;
uart->cntl = 0;
uart->lcr = MINI_UART_LCR_8BIT;
uart->mcr = 0;
uart->ier = 0;
uart->iir = MINI_UART_IIR_RX_FIFO_CLEAR |
MINI_UART_IIR_RX_FIFO_ENABLE |
MINI_UART_IIR_TX_FIFO_CLEAR |
MINI_UART_IIR_TX_FIFO_ENABLE;
uart->baud = 270; // baud rate ((250,000,000/115200)/8)-1 = 270
uart->cntl = MINI_UART_CNTL_TX_ENABLE | MINI_UART_CNTL_RX_ENABLE;
initialized = true;
}
unsigned char uart_recv(void) {
while (!uart_haschar()) ; // wait for char to arrive
return uart->data & 0xFF;
}
void uart_send(unsigned char byte) {
while (!(uart->lsr & MINI_UART_LSR_TX_EMPTY)) ;
uart->data = byte & 0xFF;
}
void uart_flush(void) {
while (!(uart->lsr & MINI_UART_LSR_TX_EMPTY)) ;
}
bool uart_haschar(void) {
return (uart->lsr & MINI_UART_LSR_RX_READY);
}
// RE: line endings
// canonial use is '\n' newline as sole line terminator (both read/write)
// but connected terminal may expect to receive a CR-LF sequence from Pi
// and may send a CR to Pi for return/enter key. uart_getchar and uart_putchar
// internally convert chars, client can simply send/receive newline
// Use uart_send/uart_recv to send/receive raw byte, no conversion
int uart_getchar(void) {
int ch = uart_recv();
if (ch == EOT) {
return EOF; // convert EOT to EOF
}
if (ch == '\r') {
return '\n'; // convert CR to newline
}
return ch;
}
int uart_putchar(int ch) {
// force initialize if not yet done
// this fallback is special case for uart_putchar as
// without it, all output (print/assert) can fail and no
// clear indication because of self-referential nature of problem
if (!initialized) uart_init();
// convert newline to CR LF sequence by inserting CR
if (ch == '\n') {
uart_send('\r');
}
uart_send(ch);
return ch;
}
int uart_putstring(const char *str) {
int n = 0;
while (str[n]) {
uart_putchar(str[n]);
n++;
}
return n;
}