Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • astro/rust-card10
  • rnestler/rust-card10
  • dbrgn/rust-card10
  • toon/rust-card10
  • mwall/rust-card10
  • puzzlewolf/rust-card10
  • rnd/rust-card10
  • lilpep/rust-card10
  • rafael/rust-card10
  • squeed/rust-card10
  • arist/rust-card10
11 results
Select Git revision
Show changes
Commits on Source (114)
Showing
with 542 additions and 216 deletions
#!/usr/bin/env bash
set -e
cargo build --release
arm-none-eabi-objcopy -O binary target/thumbv7em-none-eabi/release/$2 card10.bin
python c/bootloader/crc_patch.py card10.bin
echo "Copy this file to the device in USB Mass Storage mode:"
ls -l card10.bin
......@@ -29,4 +29,5 @@ firmware:
- apps
- main.py
- menu.py
- card10.cfg
expire_in: 1 week
From baef13b1adb6f7bf01a7dfd7f4f44a5a3e950b1d Mon Sep 17 00:00:00 2001
From: Astro <astro@spaceboyz.net>
Date: Thu, 5 Sep 2019 02:22:45 +0200
Subject: [PATCH] hack(epicardium): reduce init delay from 2s to 0.1s
---
epicardium/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/epicardium/main.c b/epicardium/main.c
index 51edbe93..7607d39d 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -32,7 +32,7 @@ int main(void)
epic_disp_print(10, 20, "Epicardium", 0xfe20, 0x0000);
epic_disp_print(off > 0 ? off : 0, 40, version_buf, 0xfe20, 0x0000);
epic_disp_update();
- mxc_delay(2000000);
+ mxc_delay(100000);
LOG_DEBUG("startup", "Initializing tasks ...");
--
2.22.1
This diff is collapsed.
......@@ -21,3 +21,6 @@ debug = true
opt-level = "s"
lto = true
panic = "abort"
[patch.crates-io]
alloc-cortex-m = { git = 'https://github.com/rust-embedded/alloc-cortex-m' }
# Rust support for the card10 CCCamp19 badge
## Prepare your card10
**Jailbreaking is no longer necessary!**
Starting with firmware v1.9, running ELF binaries requires a
`/card10.cfg` with the following content:
```
execute_elf=true
```
## Prebuilt binaries
By courtesy of this Gitlab's CI system, and NixOS, we build `.elf`
files for you drop into the `apps/` directory of your card10 badge.
Because running ELF binaries requires a jailbroken base firmware
starting with v1.5, we build this one too. It includes this project's
Rust binaries.
For each commit in this repository, we also build complete firmware
images with the required configuration and our example binaries.
https://git.card10.badge.events.ccc.de/astro/rust-card10/-/jobs
......@@ -40,25 +50,6 @@ firmware including the matching libc.
git submodule update --init --recursive
```
## Setup a Rust enabled firmware
To allow rust based apps on card10 you need a firmware which allows
to run custom elf binaries on the core. This requires a custom build
with `-Djailbreak_card10=true` as bootstrapping flag.
Assuming that you installed all required dependencies mentioned in
https://firmware.card10.badge.events.ccc.de/how-to-build.html this
should work as following:
```shell
cd card10-sys/firmware
./bootstrap -Djailbreak_card10=true
ninja -C build/
```
And then copy `build/pycardium/pycardium_epicardium.bin` as
`card10.bin` onto your badge.
## Build and run Rust loadables
### Setup
......@@ -127,14 +118,18 @@ extension (e.g `example` must be renamed as `example.elf`).
## Crates
| Crate | Description |
| ---- | --- |
| card10-sys | Unsafe C bindings for l0dables |
| card10-alloc | alloc::* support for l0dables |
| card10-l0dable | High-level crate for building l0dables |
| example | l0dable example |
| rkanoid | Arkanoid clone |
| draw-image | Example of drawing a static image to the display |
| Crate | Documentation | Description |
| ---- | --- | --- |
| card10-sys | [docs.rs][card10-sys] | Unsafe C bindings for l0dables |
| card10-alloc | [docs.rs][card10-alloc] | alloc::* support for l0dables |
| card10-l0dable | [docs.rs][card10-l0dable] | High-level crate for building l0dables |
| example | | l0dable example |
| rkanoid | | Arkanoid clone |
| draw-image | | Example of drawing a static image to the display |
[card10-sys]: https://docs.rs/card10-sys/
[card10-alloc]: https://docs.rs/card10-alloc/
[card10-l0dable]: https://docs.rs/card10-l0dable/
## Misc
......
[package]
name = "card10-alloc"
version = "0.1.0"
version = "0.1.1"
authors = ["Astro <astro@spaceboyz.net>"]
license = "MIT/Apache-2.0"
edition = "2018"
......@@ -9,6 +9,7 @@ edition = "2018"
description = "Dynamic memory allocation for card10 l0dables"
repository = "https://git.card10.badge.events.ccc.de/astro/rust-card10"
homepage = "https://git.card10.badge.events.ccc.de/astro/rust-card10"
documentation = "https://docs.rs/card10-alloc"
categories = ["no-std", "memory-management"]
keywords = [
......
//! Support for dynamically allocated memory
//!
//! Reproduces l0dable hardware.c's `_sbrk()`
//!
//! Unfortunately, we cannot link _sbrk()` directly because it
//! Unfortunately, we cannot link `_sbrk()` directly because it
//! references the unwieldy `errno`.
//!
//! ## Example
//!
//! ```rust
//! #![no_std]
//! #![no_main]
//!
//! extern crate alloc;
//! use alloc::vec;
//! use card10_l0dable::*;
//!
//! main!(main);
//! fn main() {
//! // Pass stack headroom
//! card10_alloc::init(128 * 1024);
//! let mut xs = vec![];
//! xs.push(23);
//! }
//! ```
#![no_std]
#![feature(asm)]
#![feature(alloc_error_handler)]
......@@ -26,7 +47,10 @@ fn sp() -> usize {
value
}
/// You must call this before using anything from `alloc`.
/// Call this before using anything from `alloc`.
///
/// Consider the size of your stack-allocated variables for the
/// `stack_headroom` parameter.
///
/// Returns heap size
pub fn init(stack_headroom: usize) -> usize {
......
[package]
edition = "2018"
name = "card10-l0dable"
version = "0.1.1"
authors = ["Astro <astro@spaceboyz.net>", "Kloenk <me@kloenk.de>"]
version = "0.3.0"
authors = [
"Astro <astro@spaceboyz.net>",
"Kloenk <me@kloenk.de>",
"Bruno Kirschner <bruno.kirschner@online.de>",
"Puzzlewolf <camp@nora.pink>",
"Raphael Nestler <raphael.nestler@gmail.com>",
"Danilo Bargen <mail@dbrgn.ch>",
"toon <toon@c3d2.de>",
"Sergey Pepyakin <sergei@parity.io>",
]
license = "MIT/Apache-2.0"
repository = "https://git.card10.badge.events.ccc.de/astro/rust-card10"
homepage = "https://git.card10.badge.events.ccc.de/astro/rust-card10"
......@@ -15,8 +25,8 @@ keywords = [
]
categories = ["no-std"]
description = "make l0dables for the Card10 (CCCamp 2019) badge"
documentation = "https://docs.rs/card10-l0dable"
[dependencies]
card10-sys = { path = "../card10-sys", version = "^0.1" }
embedded-graphics = "0.5.2"
card10-sys = { path = "../card10-sys" }
embedded-graphics = "0.5"
/// Accelerometer, Gyroscope, Orientation
use core::{
fmt::{self, Display, Write},
marker::PhantomData,
......@@ -53,6 +55,10 @@ pub struct Sensor<S: SensorType> {
}
impl<S: SensorType> Sensor<S> {
/// Use one of:
/// - `BHI160::<Accelerometer>::start()`
/// - `BHI160::<Gyroscope>::start()`
/// - `BHI160::<Orientation>::start()`
fn new(stream_id: i32) -> Self {
Self {
stream_id,
......
/// Enviromental sensor
use card10_sys::*;
use core::mem::uninitialized;
......@@ -7,7 +9,7 @@ pub type SensorData = bme680_sensor_data;
impl BME680 {
pub fn start() -> Self {
if unsafe { epic_bme680_init() } != 0 {
panic!("Cannot start light sensor");
panic!("Cannot start BME680 sensor");
}
BME680
}
......
use card10_sys::*;
/// Button inputs
pub struct Buttons {
state: u32,
}
impl Buttons {
/// Read the current button state
pub fn read() -> Self {
let mask = epic_button_BUTTON_LEFT_BOTTOM
| epic_button_BUTTON_RIGHT_BOTTOM
......
......@@ -49,7 +49,7 @@ impl Color {
}
pub fn g8(&self) -> u8 {
self.r() << 2
self.g() << 2
}
pub fn b(&self) -> u8 {
......@@ -57,7 +57,7 @@ impl Color {
}
pub fn b8(&self) -> u8 {
self.r() << 3
self.b() << 3
}
}
......@@ -75,12 +75,23 @@ pub enum FillStyle {
pub struct Display;
#[repr(u8)]
pub enum Font {
Font8 = disp_font_name_DISP_FONT8 as u8,
Font12 = disp_font_name_DISP_FONT12 as u8,
Font16 = disp_font_name_DISP_FONT16 as u8,
Font20 = disp_font_name_DISP_FONT20 as u8,
Font24 = disp_font_name_DISP_FONT24 as u8,
}
/// Immediate mode routines
impl Display {
pub const W: u16 = 160;
pub const H: u16 = 80;
pub const FONT_W: u16 = 14;
pub const FONT_H: u16 = 20;
pub const W: i16 = 160;
pub const H: i16 = 80;
pub const FONT_W: i16 = 14;
pub const FONT_H: i16 = 20;
/// Open the display, return an instance
pub fn open() -> Self {
unsafe {
epic_disp_open();
......@@ -88,37 +99,52 @@ impl Display {
Display
}
/// Write Epicardium's framebuffer to the display
pub fn update(&self) {
unsafe {
epic_disp_update();
}
}
/// Clear everything with a solid `color`
pub fn clear(&self, color: Color) {
unsafe {
epic_disp_clear(color.0);
}
}
/// Print text
///
/// s must be 0-terminated
pub fn print(&self, x: u16, y: u16, s: &[u8], fg: Color, bg: Color) {
pub fn print(&self, x: i16, y: i16, s: &[u8], fg: Color, bg: Color) {
unsafe {
epic_disp_print(x, y, s.as_ptr(), fg.0, bg.0);
}
}
pub fn pixel(&self, x: u16, y: u16, color: Color) {
/// Print text with a selected font
///
/// s must be 0-terminated
pub fn print_adv(&self, font: Font, x: i16, y: i16, s: &[u8], fg: Color, bg: Color) {
unsafe {
epic_disp_print_adv(font as u8, x, y, s.as_ptr(), fg.0, bg.0);
}
}
/// Set a pixel
pub fn pixel(&self, x: i16, y: i16, color: Color) {
unsafe {
epic_disp_pixel(x, y, color.0);
}
}
/// Draw a line
pub fn line(
&self,
x1: u16,
y1: u16,
x2: u16,
y2: u16,
x1: i16,
y1: i16,
x2: i16,
y2: i16,
color: Color,
linestyle: LineStyle,
pixelsize: u16,
......@@ -130,10 +156,10 @@ impl Display {
pub fn rect(
&self,
x1: u16,
y1: u16,
x2: u16,
y2: u16,
x1: i16,
y1: i16,
x2: i16,
y2: i16,
color: Color,
fillstyle: FillStyle,
pixelsize: u16,
......@@ -143,10 +169,11 @@ impl Display {
}
}
/// Draw a circle
pub fn circ(
&self,
x: u16,
y: u16,
x: i16,
y: i16,
rad: u16,
color: Color,
fillstyle: FillStyle,
......@@ -157,7 +184,11 @@ impl Display {
}
}
pub fn framebuffer<'d>(&'d self) -> FrameBuffer<'d> {
/// Obtain a handle for a framebuffer.
///
/// Don't use `display.send()` but `framebuffer.send()` as long as
/// it is in use.
pub fn framebuffer<'d>(&'d mut self) -> FrameBuffer<'d> {
FrameBuffer::uninitialized(self)
}
}
......@@ -169,3 +200,42 @@ impl Drop for Display {
}
}
}
/// Convenience text display
///
/// Requires `card10_alloc::init()` and `extern crate alloc;`
#[macro_export]
macro_rules! display {
($disp: expr, $x: expr, $y: expr, $fg: expr, $bg: expr,
$fmt: expr) => ({
use alloc::format;
let s = format!(concat!($fmt, "\0"));
$disp.print($x, $y, s.as_bytes(), $fg, $bg);
});
($disp: expr, $x: expr, $y: expr, $fg: expr, $bg: expr,
$fmt: expr, $($arg: tt)*) => ({
use alloc::format;
let s = format!(concat!($fmt, "\0"), $($arg)*);
$disp.print($x, $y, s.as_bytes(), $fg, $bg);
});
}
/// Convenience text display with selected font
///
/// Requires `card10_alloc::init()` and `extern crate alloc;`
#[macro_export]
macro_rules! display_adv {
($disp: expr, $font: tt, $x: expr, $y: expr, $fg: expr, $bg: expr,
$fmt: expr) => ({
use alloc::format;
use card10_l0dable::Font;
let s = format!(concat!($fmt, "\0"));
$disp.print_adv(Font::$font, $x, $y, s.as_bytes(), $fg, $bg);
});
($disp: expr, $font: tt, $x: expr, $y: expr, $fg: expr, $bg: expr,
$fmt: expr, $($arg: tt)*) => ({
use alloc::format;
let s = format!(concat!($fmt, "\0"), $($arg)*);
$disp.print_adv(Font::$font, $x, $y, s.as_bytes(), $fg, $bg);
});
}
......@@ -3,7 +3,7 @@ use card10_sys::*;
use core::mem::{transmute, uninitialized};
use core::ops::{Index, IndexMut};
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::pixelcolor::{PixelColor, Rgb565};
use embedded_graphics::prelude::Pixel;
use embedded_graphics::Drawing;
......@@ -13,12 +13,12 @@ mod text;
pub use text::TextRenderer;
pub struct FrameBuffer<'d> {
_display: &'d Display,
_display: &'d mut Display,
buffer: disp_framebuffer,
}
impl<'d> FrameBuffer<'d> {
pub fn uninitialized(display: &'d Display) -> Self {
pub fn uninitialized(display: &'d mut Display) -> Self {
FrameBuffer {
_display: display,
buffer: unsafe { uninitialized() },
......@@ -61,37 +61,40 @@ impl<'d> FrameBuffer<'d> {
impl<'d> Index<(u16, u16)> for FrameBuffer<'d> {
type Output = RawColor;
fn index(&self, (x, y): (u16, u16)) -> &Self::Output {
let x = usize::from(Display::W - 1 - x);
let y = usize::from(Display::H - 1 - y);
let x = usize::from(Display::W as u16 - 1 - x);
let y = usize::from(Display::H as u16 - 1 - y);
unsafe { transmute(&self.buffer.fb[y][x]) }
}
}
impl<'d> IndexMut<(u16, u16)> for FrameBuffer<'d> {
fn index_mut(&mut self, (x, y): (u16, u16)) -> &mut Self::Output {
let x = usize::from(Display::W - 1 - x);
let y = usize::from(Display::H - 1 - y);
let x = usize::from(Display::W as u16 - 1 - x);
let y = usize::from(Display::H as u16 - 1 - y);
unsafe { transmute(&mut self.buffer.fb[y][x]) }
}
}
impl<'d> Drawing<Rgb565> for FrameBuffer<'d> {
/// `embedded-graphics` support
impl<'d, C: PixelColor + Into<RawColor>> Drawing<C> for FrameBuffer<'d> {
fn draw<T>(&mut self, item: T)
where
T: IntoIterator<Item = Pixel<Rgb565>>,
T: IntoIterator<Item = Pixel<C>>,
{
for Pixel(coord, color) in item {
let x = coord[0] as u16;
let y = coord[1] as u16;
if x >= Display::W || y >= Display::H {
if x >= Display::W as u16 || y >= Display::H as u16 {
continue;
}
self[(x, y)] = RawColor::rgb8(color.r(), color.g(), color.b());
// Swap bytes
self[(x, y)] = color.into();
}
}
}
/// RGB565 with swapped bytes
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct RawColor([u8; 2]);
......@@ -121,9 +124,40 @@ impl RawColor {
Self::rgb8(0xff, 0xff, 0)
}
/// Construct from 0..255-triple
pub fn rgb8(r8: u8, g8: u8, b8: u8) -> Self {
let b1 = (r8 & 0xF8) | (g8 >> 5);
let b2 = ((g8 & 0xFA) << 3) | (b8 >> 3);
RawColor([b1, b2])
}
pub fn r8(&self) -> u8 {
self.0[0] & 0xF8
}
pub fn g8(&self) -> u8 {
((self.0[0] & 0x7) << 5) |
((self.0[0] & 0xE) >> 3)
}
pub fn b8(&self) -> u8 {
(self.0[1] & 0x1F) << 3
}
}
impl PixelColor for RawColor {}
/// Most similar format, just byte-swapped
impl From<Rgb565> for RawColor {
fn from(rgb: Rgb565) -> Self {
let c = rgb.0;
RawColor([(c >> 8) as u8, c as u8])
}
}
/// Dummy implementation required for impl Drawable
impl From<u8> for RawColor {
fn from(b: u8) -> Self {
RawColor([0, b])
}
}
......@@ -24,10 +24,10 @@ impl<'a, 'd, 'f> Write for TextRenderer<'a, 'd, 'f> {
Some(glyph) => {
for y in 0..self.font.h {
let y1 = (self.y + y as isize) as u16;
if y1 < Display::H {
if y1 < Display::H as u16 {
for x in 0..self.font.w {
let x1 = (self.x + x as isize) as u16;
if x1 < Display::W {
if x1 < Display::W as u16 {
if glyph.get_pixel(x as usize, y as usize) {
self.framebuffer[(x1, y1)] = self.color;
}
......
//! File System support (unfinished)
use core::mem::uninitialized;
use core::str::from_utf8_unchecked;
......
//! Lights
use card10_sys::*;
#[derive(Clone, Copy)]
pub enum LEDColor {
RGB(u8, u8, u8),
HSV(f32, f32, f32),
}
/// Update all RGB LEDs
///
/// `f` must supply a `LEDColor` for `0..=10`.
pub fn update_rgb_leds<F>(f: F)
where
F: Fn(i32) -> LEDColor,
{
for index in 0..=10 {
let color = f(index);
match color {
LEDColor::RGB(r, g, b) => unsafe {
epic_leds_prep(index, r, g, b);
}
LEDColor::HSV(h, s, v) => unsafe {
epic_leds_prep_hsv(index, h, s, v);
}
}
unsafe {
epic_leds_update();
}
}
}
/// Set powersave mode for the RGB leds
///
/// Setting powersave mode disables the LEDs, saving
/// ~15 mA
pub fn set_rgb_leds_powersave(v: bool) {
unsafe { epic_leds_set_powersave(v); }
}
#[repr(i32)]
pub enum Rocket {
Blue = 0,
Yellow = 1,
Green = 2,
}
/// Set one of the rocket LEDs to the desired brightness.
///
/// Brightness should be 0 - 31; larger values will be clamped.
pub fn set_rocket(r: Rocket, brightness: u8) {
let v = if brightness > 31{31} else {brightness}; // clamp
unsafe { epic_leds_set_rocket(r as i32, v) }
}
//! [Repository](https://git.card10.badge.events.ccc.de/astro/rust-card10)
#![no_std]
mod os;
pub use os::*;
mod display;
pub use display::{Color, Display, FillStyle, LineStyle};
pub use display::{Color, Display, FillStyle, Font, LineStyle};
mod buttons;
pub mod framebuffer;
pub use buttons::Buttons;
......@@ -19,12 +20,16 @@ mod fmt_buffer;
pub use fmt_buffer::{str_to_cstr, FmtBuffer};
mod bme680;
pub use bme680::BME680;
mod max86150;
pub use max86150::MAX86150;
mod bhi160;
pub use bhi160::{
Accelerometer, Error as BHI160Error, Gyroscope, Orientation, Sensor as BHI160,
SensorData as BHI160Data,
};
pub mod fs;
mod leds;
pub use leds::*;
/// Type check the user-supplied entry function.
#[macro_export]
......
use card10_sys::*;
/// Light sensor
pub struct LightSensor {
// Prevent creation of this struct by all but this module.
_private: (),
}
impl LightSensor {
/// Start sensing light
pub fn start() -> Self {
if unsafe { epic_light_sensor_run() } != 0 {
panic!("Cannot start light sensor");
......@@ -13,6 +15,7 @@ impl LightSensor {
LightSensor { _private: () }
}
/// Obtain current light sensor reading
pub fn get(&self) -> Option<u16> {
let mut result = 0;
if unsafe { epic_light_sensor_get(&mut result) } == 0 {
......
// ECG and PPG sensor
pub struct MAX86150 {
stream_id: i32,
}
use core::{mem::size_of, mem::MaybeUninit};
use card10_sys::*;
// TODO: redefine in common sensor
use crate::bhi160::Error;
const DATA_MAX: usize = 128;
impl MAX86150 {
pub fn start() -> Result<Self, Error> {
let mut cfg = max86150_sensor_config {
sample_buffer_len: DATA_MAX,
ppg_sample_rate: 200,
};
let stream_id =
unsafe { epic_max86150_enable_sensor(&mut cfg, size_of::<max86150_sensor_config>()) };
if stream_id < 0 {
let error = match -stream_id {
errno::EBUSY => Error::DriverBusy,
_ => Error::Unknown(stream_id),
};
return Err(error);
}
Ok(MAX86150 { stream_id })
}
pub fn read(&self) -> Result<MAX86150SensorData, Error> {
let mut buffer = MaybeUninit::<[max86150_sensor_data; DATA_MAX]>::zeroed();
let buffer_pointer = buffer.as_mut_ptr() as *mut _;
let packet_count = unsafe {
epic_stream_read(
self.stream_id,
buffer_pointer,
size_of::<max86150_sensor_data>() * DATA_MAX,
)
};
if packet_count < 0 {
let error = match -packet_count {
errno::ENODEV => Error::SensorUnavailable,
errno::EBADF => Error::SensorDescriptorUnknown,
errno::EINVAL => Error::InvalidSampleCount,
errno::EBUSY => Error::CouldNotAcquireLock,
_ => Error::Unknown(packet_count),
};
return Err(error);
}
Ok(MAX86150SensorData {
buf: unsafe { buffer.assume_init() },
n: packet_count as usize,
})
}
}
pub struct MAX86150SensorData {
buf: [max86150_sensor_data; DATA_MAX],
n: usize,
}
impl MAX86150SensorData {
pub const fn is_empty(&self) -> bool {
self.n == 0
}
pub const fn len(&self) -> usize {
self.n
}
}
impl<'a> IntoIterator for &'a MAX86150SensorData {
type Item = MAX86150SensorDataItem;
type IntoIter = MAX86150SensorDataIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
MAX86150SensorDataIterator { data: self, pos: 0 }
}
}
pub struct MAX86150SensorDataIterator<'a> {
data: &'a MAX86150SensorData,
pos: usize,
}
#[derive(Debug, Clone)]
pub struct MAX86150SensorDataItem {
pub red_raw: u32,
pub ir_raw: u32,
pub ecg_raw: i32,
}
impl MAX86150SensorDataItem {
pub fn get_red(&self) -> u32 {
self.red_raw
}
}
impl<'a> Iterator for MAX86150SensorDataIterator<'a> {
type Item = MAX86150SensorDataItem;
fn next(&mut self) -> Option<Self::Item> {
while self.pos < self.data.n {
let vec = &self.data.buf[self.pos];
let item = MAX86150SensorDataItem {
red_raw: vec.red,
ir_raw: vec.ir,
ecg_raw: vec.ecg,
};
self.pos += 1;
return Some(item);
}
None
}
}
impl Drop for MAX86150 {
fn drop(&mut self) {
unsafe {
epic_max86150_disable_sensor();
}
}
}