Skip to content

Commit

Permalink
Add basic Sense HAT Library
Browse files Browse the repository at this point in the history
Just has an `set_pixels` function for writing to the entire LED matrix
via I2C for now.
  • Loading branch information
AlexJones0 committed Feb 7, 2025
1 parent 0c73e34 commit 8f93656
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 0 deletions.
74 changes: 74 additions & 0 deletions libraries/sense_hat.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright lowRISC Contributors.
// SPDX-License-Identifier: Apache-2.0

#include "sense_hat.hh"
#include <cheri.hh>
#include <platform-i2c.hh>

using namespace CHERI;

/**
* Helper. Returns a pointer to the I2C device.
*/
[[nodiscard, gnu::always_inline]] static Capability<volatile OpenTitanI2c> i2c()
{
return MMIO_CAPABILITY(OpenTitanI2c, i2c1);
}

void __cheri_libcall Internal::init_i2c()
{
/* Increase the reliability of the Sense HAT I2C against controller halts
in case the I2C Controller gets into a bad state while loading demos. */
i2c()->control = i2c()->control & ~(OpenTitanI2c::ControlEnableHost |
OpenTitanI2c::ControlEnableTarget);
if (i2c()->interrupt_is_asserted(OpenTitanI2cInterrupt::ControllerHalt))
{
i2c()->reset_controller_events();
}

/* Initialise the I2C controller as normal. */
i2c()->reset_fifos();
i2c()->host_mode_set();
i2c()->speed_set(100);
}

bool __cheri_libcall SenseHat::set_pixels(Colour pixels8x8[64])
{
uint8_t writeBuffer[1 + 64 * 3] = {0x0u};
uint32_t i = 0u;
writeBuffer[i++] = 0x00u; // Address
for (uint8_t row = 0u; row < 8u; row++)
{
for (uint8_t column = 0u; column < 8u; column++)
{
uint32_t index = row * 8u + column;
if (pixels8x8[index].red > Colour::MaxRedValue)
{
Debug::log("{}:{} exceeds maximum red.", row, column);
return false;
}
writeBuffer[i++] = pixels8x8[index].red;
}
for (uint8_t column = 0u; column < 8u; column++)
{
uint32_t index = row * 8u + column;
if (pixels8x8[index].green > Colour::MaxGreenValue)
{
Debug::log("{}:{} exceeds maximum green.", row, column);
return false;
}
writeBuffer[i++] = pixels8x8[index].green;
}
for (uint8_t column = 0u; column < 8u; column++)
{
uint32_t index = row * 8u + column;
if (pixels8x8[index].blue > Colour::MaxBlueValue)
{
Debug::log("{}:{} exceeds maximum blue.", row, column);
return false;
}
writeBuffer[i++] = pixels8x8[index].blue;
}
}
return i2c()->blocking_write(0x46u, writeBuffer, sizeof(writeBuffer), true);
}
73 changes: 73 additions & 0 deletions libraries/sense_hat.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright lowRISC Contributors.
// SPDX-License-Identifier: Apache-2.0

/*
* A driver used for writing to the Raspberry Pi Sense HAT LED Matrix
* via an I2C connection.
*
* Warning: Aborting the I2C transaction (e.g. resetting the FPGA, switching
* software slot or bitstream) can cause the in-progress I2C transaction which
* is writing the entire framebuffer to be interrupted. This can leave the I2C
* controller on the Sense HAT in a bad state, with no easy mechanism for
* resetting it via the OpenTitan I2C controller. You should be aware of this
* if you plan to use and rely on this driver. If the I2C controller does get
* into a bad state, you must unplug (for a few seconds) and replug the Sense
* HAT, or power cycle the entire Sonata FPGA. Do not use this driver if you
* plan to switch software slots / bitstreams regularly.
*/

#include <algorithm>
#include <debug.hh>
#include <utility>

namespace Internal
{
void __cheri_libcall init_i2c();
} // namespace Internal

class SenseHat
{
private:
/// Flag set when we're debugging this driver.
static constexpr bool DebugSenseHat = true;

/// Helper for conditional debug logs and assertions.
using Debug = ConditionalDebug<DebugSenseHat, "Sense HAT">;

public:
struct Colour
{
// Sense HAT LED Matrix uses RGB 565
static constexpr uint8_t RedBits = 5;
static constexpr uint8_t GreenBits = 6;
static constexpr uint8_t BlueBits = 5;

// Maximum values for each field, calculated from the number of bits
static constexpr uint8_t MaxRedValue = (1 << RedBits) - 1;
static constexpr uint8_t MaxGreenValue = (1 << GreenBits) - 1;
static constexpr uint8_t MaxBlueValue = (1 << BlueBits) - 1;

// Colour fields
uint8_t red;
uint8_t green;
uint8_t blue;
} __attribute__((packed));

/**
* A constructor for the Sense HAT driver. Initialises the I2C controller
* that will communicate with the Sense HAT's I2C Controller to write to
* its LED Matrix.
*/
SenseHat()
{
Internal::init_i2c();
}

/**
* Set the values of all pixels in the 8x8 LED Matrix on the Sense HAT
* via an I2C connection. Will block until the entire array has been
* written. Values are read from `pixels8x8` in row-major order (i.e.
* all of row 1, then all of row 2, etc.)
*/
bool __cheri_libcall set_pixels(Colour pixels8x8[64]);
};
4 changes: 4 additions & 0 deletions libraries/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ library("lcd")
add_files("../third_party/display_drivers/core/lucida_console_12pt.c")
add_files("../third_party/display_drivers/st7735/lcd_st7735.c")
add_files("lcd.cc")

library("sense_hat")
set_default(false)
add_files("sense_hat.cc")

0 comments on commit 8f93656

Please sign in to comment.