Giter VIP home page Giter VIP logo

driver-examples's Introduction

Additional example programs for several rust drivers

Build Status

This repository includes examples of using devices through these drivers:

Device driver Description Interface Introductory blog post
Ad983x Waveform generator / direct digital synthesizer (DDS). SPI Intro blog post
Ads1x1x 12/16-bit Analog-to-digital (ADC) converters. I2C Intro blog post
Apds9960 Digital proximity, ambient light, RGB and gesture sensor. I2C
Bmi160 Inertial measurement unit (IMU) (accelerometer + gyro). I2C / SPI
Ds1307 Real-time clock (RTC) / calendar. I2C Intro blog post
Ds323x Extremely accurate real-time clock (RTC) / calendar. I2C / SPI
Eeprom24x 24x series serial EEPROM devices. I2C Intro blog post
Embedded-Ccs811 Digital gas sensor for monitoring indoor air quality. I2C Intro blog post
Hdc20xx Temperature and humidity sensors. I2C
iAQ-Core Indoor air quality sensor. I2C
Isl29125 RGB color light sensor with IR blocking filter. I2C
Kxcj9 Tri-axis MEMS accelerometer. I2C Intro blog post
Lm75 Temperature sensor and thermal watchdog. I2C
Lsm303agr Tri-axis accelerometer and tri-axis magnetometer. I2C / SPI
Max170xx Fuel-gauge for lithium-ion (Li+) batteries. I2C
Max3010x Pulse oximeter and heart-rate sensor. I2C
Max44009 Ambient light sensor. I2C
Mcp4x Digital potentiometers. SPI
Mcp49xx 8/10/12-bit Digital-to-analog (DAC) converters. SPI
Mcp794xx Real-time clock (RTC) / calendar. I2C Intro blog post
Mlx9061x Non-contact infrared (IR) thermometer. I2C
Mma8x5x Tri-axis MEMS accelerometers. I2C
Opt300x Ambient light sensor. I2C Intro blog post
Pcf857x 8/16-pin I/O port expanders. I2C
Pwm-Pca9685 16-pin PWM port expander / LED driver. I2C Intro blog post
Si4703 FM radio turners (receivers). I2C Intro blog post
Tcs3472 RGBW light color sensor with IR filter. I2C
Tmp006 Non-contact infrared (IR) thermopile temperature sensor. I2C Intro blog post
Tmp1x2 Temperature sensors. I2C Intro blog post
Veml6030 Ambient light sensor. I2C Intro blog post
Veml6040 RGBW light color sensor. I2C
Veml6070 Ultraviolet A (UVA) light sensor. I2C
Veml6075 Ultraviolet A (UVA) and B (UVB) light sensor. I2C Intro blog post
W25 Winbond's W25 serial flash memory devices. SPI
Xca9548a TCA9548A/PCA9548A I2C switches/multiplexers. I2C

These examples use several boards: STM32F3-Discovery, STM32F103 "Blue pill", Raspberry Pi and Micro:bit V2. These are classified in different folders.

At the beginning of each example the setup and behavior is described. Many of them also use an SSD1306 OLED display. You can get most of the modules used here on AliExpress generally for a very small price.

These examples are guaranteed to build with the latest Rust stable release. If you get a build error, try updating your Rust installation.

To run the examples, clone this repository, go to the appropriate folder and run either cargo embed or cargo run. Look in the README of each folder for instructions.

git clone https://github.com/eldruin/driver-examples
cd driver-examples/stm32f1-bluepill
# ...

License

Licensed under either of:

at your option.

Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

driver-examples's People

Contributors

eldruin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

driver-examples's Issues

Request: add some ESP32 examples

Would be awesome to see some examples for the ESP32, as it's a very popular ecosystem, and its Rust support seems pretty strong.

The LM75 temperature sensor would be a good first example to add!

type mismatch / trait problems

As a learning exercise I have being trying to organizing some of these examples with a setup() function for the HAL/hardware specific part of the code. This makes it easier to generalize the example to use with other boards. (More on that when I get further.) The example si4703-fm-radio-display-bp is a bit difficult because of the seek control pins in addition to the i2c. This code seems like it is almost working but I am having type mismatch / trait problems

Click to expand
#![deny(unsafe_code)]
#![no_std]
#![no_main]

use core::fmt::Write;
use cortex_m_rt::entry;
use embedded_graphics::{
    fonts::{Font6x8, Text},
    pixelcolor::BinaryColor,
    prelude::*,
    style::TextStyleBuilder,
};
use embedded_hal::digital::v2::{InputPin, OutputPin};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use si4703::{
    reset_and_select_i2c_method1 as reset_si4703, ChannelSpacing, DeEmphasis, ErrorWithPin,
    SeekDirection, SeekMode, Si4703, Volume,
};

use ssd1306::{prelude::*, Builder, I2CDIBuilder};

pub trait LED {
    // depending on board wiring, on may be set_high or set_low, with off also reversed
    // implementation should deal with this difference
    fn on(&mut self) -> ();
    fn off(&mut self) -> ();

    // default methods
    fn blink(&mut self, time: u16, delay: &mut Delay) -> () {
        self.on();
        delay.delay_ms(time);
        self.off();
        delay.delay_ms(time); // consider delay.delay_ms(500u16);
    }
}

pub struct SeekPins<T, U> {
    p_seekup:   T,
    p_seekdown: U,
    //p_stcint:   V,
}

pub trait SEEK {
    fn seekup(&mut self)   -> bool;
    fn seekdown(&mut self) -> bool;
    //fn stcint(&mut self) -> ;
}

// setup() does all  hal/MCU specific setup and returns generic hal device for use in main code.
use stm32f1xx_hal::{
    delay::Delay,
    gpio::{
        gpiob::{PB10, PB11, PB6},
        gpioc::PC13,
        Input, Output, PullDown, PullUp, PushPull,
    },
    i2c::{BlockingI2c, DutyCycle, Mode, Pins},
    pac::{CorePeripherals, Peripherals, I2C1},
    prelude::*,
};

fn setup() -> (
    BlockingI2c<I2C1, impl Pins<I2C1>>,
    impl LED,
    Delay,
    impl SEEK,
    PB6<Input<PullUp>>,
) {
    let cp = CorePeripherals::take().unwrap();
    let dp = Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();

    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);

    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);

    let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
    //let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);  //OutputPin is not implemented
    let sda = gpiob.pb9.into_push_pull_output(&mut gpiob.crh);        //trait Pins<I2C1> not implemented

    let i2c = BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );

    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
    let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    let delay = Delay::new(cp.SYST, clocks);

    impl LED for PC13<Output<PushPull>> {
        fn on(&mut self) -> () {
            self.set_low().unwrap()
        }
        fn off(&mut self) -> () {
            self.set_high().unwrap()
        }
    }

    let stcint   = gpiob.pb6.into_pull_up_input(&mut gpiob.crl);

    let buttons: SeekPins<PB10<Input<PullDown>>, PB11<Input<PullDown>>> = SeekPins{
        p_seekup   : gpiob.pb10.into_pull_down_input(&mut gpiob.crh), 
        p_seekdown : gpiob.pb11.into_pull_down_input(&mut gpiob.crh)
    };
    
    impl  SEEK for SeekPins<PB10<Input<PullDown>>, PB11<Input<PullDown>>> {
        fn seekup(&mut self) -> bool {
            self.p_seekup.is_high().unwrap()
        }
        fn seekdown(&mut self) -> bool {
            self.p_seekdown.is_high().unwrap()
        }
    }

    let mut rst = gpiob.pb7.into_push_pull_output(&mut gpiob.crl);
    //let z = reset_si4703(&mut rst, &mut sda, &mut delay).unwrap();
    let _z = reset_si4703(&mut rst, &mut sda, &mut delay);

    (i2c, led, delay, buttons, stcint)
}

// End of hal/MCU specific setup. Following should be generic code.

#[entry]
fn main() -> ! {
    rtt_init_print!();
    rprintln!("Si4703 example");

    let (i2c, mut led, mut delay, mut buttons, mut stcint) = setup();

    let manager = shared_bus::BusManager::<cortex_m::interrupt::Mutex<_>, _>::new(i2c);
    let interface = I2CDIBuilder::new().init(manager.acquire());
    let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();
    disp.init().unwrap();
    disp.flush().unwrap();

    let text_style = TextStyleBuilder::new(Font6x8)
        .text_color(BinaryColor::On)
        .build();

    let mut radio = Si4703::new(manager.acquire());
    radio.enable_oscillator().unwrap();
    delay.delay_ms(500_u16);
    radio.enable().unwrap();
    delay.delay_ms(110_u16);

    radio.set_volume(Volume::Dbfsm28).unwrap();
    radio.set_deemphasis(DeEmphasis::Us50).unwrap();
    radio.set_channel_spacing(ChannelSpacing::Khz100).unwrap();
    radio.unmute().unwrap();

    let mut buffer: heapless::String<64> = heapless::String::new();
    loop {
        // Blink LED 0 every time a new seek is started
        // to check that everything is actually running.
        led.blink(50_u16, &mut delay);

        let should_seek_down = buttons.seekdown();
        let should_seek_up   = buttons.seekup();
        if should_seek_down || should_seek_up {
            buffer.clear();
            write!(buffer, "Seeking...").unwrap();

            disp.clear();
            Text::new(&buffer, Point::zero())
                .into_styled(text_style)
                .draw(&mut disp)
                .unwrap();

            disp.flush().unwrap();
            let direction = if should_seek_down {
                SeekDirection::Down
            } else {
                SeekDirection::Up
            };

            buffer.clear();
            loop {
                match radio.seek_with_stc_int_pin(SeekMode::Wrap, direction, &stcint) {
                    Err(nb::Error::WouldBlock) => {}
                    Err(nb::Error::Other(ErrorWithPin::SeekFailed)) => {
                        write!(buffer, "Seek Failed!  ").unwrap();
                        break;
                    }
                    Err(_) => {
                        write!(buffer, "Error!     ").unwrap();
                        break;
                    }
                    Ok(_) => {
                        let channel = radio.channel().unwrap_or(-1.0);
                        write!(buffer, "Found {:1} MHz ", channel).unwrap();
                        break;
                    }
                }
            }
            disp.clear();
            Text::new(&buffer, Point::zero())
                .into_styled(text_style)
                .draw(&mut disp)
                .unwrap();

            disp.flush().unwrap();
        }
    }
}

with the code as shown, sda as in your code, I get

error[E0277]: the trait bound `(PB8<Alternate<OpenDrain>>, PB9<Output<PushPull>>): stm32f1xx_hal::i2c::Pins<I2C1>` is not satisfied
  --> examples/zz.rs:87:15
   |
87 |     let i2c = BlockingI2c::i2c1(
   |               ^^^^^^^^^^^^^^^^^ the trait `stm32f1xx_hal::i2c::Pins<I2C1>` is not implemented for `(PB8<Alternate<OpenDrain>>, PB9<Output<PushPull>>)`
   |
   = help: the following implementations were found:
             <(PB10<Alternate<OpenDrain>>, PB11<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C2>>
             <(PB6<Alternate<OpenDrain>>, PB7<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C1>>
             <(PB8<Alternate<OpenDrain>>, PB9<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C1>>
   = note: required by `BlockingI2c::<I2C1, PINS>::i2c1`

error[E0277]: the trait bound `(PB8<Alternate<OpenDrain>>, PB9<Output<PushPull>>): stm32f1xx_hal::i2c::Pins<I2C1>` is not satisfied
  --> examples/zz.rs:65:23
   |
65 |     BlockingI2c<I2C1, impl Pins<I2C1>>,
   |                       ^^^^^^^^^^^^^^^ the trait `stm32f1xx_hal::i2c::Pins<I2C1>` is not implemented for `(PB8<Alternate<OpenDrain>>, PB9<Output<PushPull>>)`
   |
   = help: the following implementations were found:
             <(PB10<Alternate<OpenDrain>>, PB11<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C2>>
             <(PB6<Alternate<OpenDrain>>, PB7<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C1>>
             <(PB8<Alternate<OpenDrain>>, PB9<Alternate<OpenDrain>>) as stm32f1xx_hal::i2c::Pins<I2C1>>

error: aborting due to 2 previous errors


and with

let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);  //OutputPin is not implemented

I get

error[E0271]: type mismatch resolving `<PB9<Alternate<OpenDrain>> as embedded_hal::digital::v2::OutputPin>::Error == Infallible`
   --> examples/zz.rs:134:14
    |
134 |     let _z = reset_si4703(&mut rst, &mut sda, &mut delay);
    |              ^^^^^^^^^^^^ expected enum `Infallible`, found `()`
    | 
   ::: /home/paul/.cargo/git/checkouts/si4703-rs-df77d9d8ce5b9ec0/7d40805/src/reset.rs:12:20
    |
12  |     SDA: OutputPin<Error = E>,
    |                    --------- required by this bound in `reset_and_select_i2c_method1`

error[E0277]: the trait bound `PB9<Alternate<OpenDrain>>: embedded_hal::digital::OutputPin` is not satisfied
   --> examples/zz.rs:134:14
    |
134 |     let _z = reset_si4703(&mut rst, &mut sda, &mut delay);
    |              ^^^^^^^^^^^^ the trait `embedded_hal::digital::OutputPin` is not implemented for `PB9<Alternate<OpenDrain>>`
    | 
   ::: /home/paul/.cargo/git/checkouts/si4703-rs-df77d9d8ce5b9ec0/7d40805/src/reset.rs:12:10
    |
12  |     SDA: OutputPin<Error = E>,
    |          -------------------- required by this bound in `reset_and_select_i2c_method1`
    |
    = note: required because of the requirements on the impl of `embedded_hal::digital::v2::OutputPin` for `PB9<Alternate<OpenDrain>>`

error: aborting due to 2 previous errors

I do not have this problem with i2c in the other examples, in which I use

let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);

I am using an up-to-date fork of driver-examples and current github version of stm32f1xx_hal.

Suggestions?

(BTW, I am not really happy with my struct seekPins and trait SEEK, I think they should be tied more closely to radio. Suggestions on that also appreciated.)

heapless problems

There seems to be a problem with heapless 0.7 not compiling, which affects many (all?) examples:

driver-examples/stm32f1-bluepill $ cargo embed --example ads1015-adc-display-bp
   Compiling generic-array v0.14.4
...
error: could not compile `heapless`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed
error[E0658]: const generics are unstable
  --> /home/paul/.cargo/registry/src/github.com-1ecc6299db9ec823/heapless-0.7.0/src/histbuf.rs:37:35
...

If I change Cargo.toml to

heapless =  ">=0.4.3, <0.7.0"  # "0.7"

then there is a problem with driver examples:

cargo embed --example ads1015-adc-display-bp
   Compiling driver-examples-bluepill v0.1.0 (/home/paul/githubClones/driver-examples/stm32f1-bluepill)
error: could not compile `driver-examples-bluepill`

To learn more, run the command again with --verbose.
error[E0747]: constant provided when a type was expected
   --> examples/ads1015-adc-display-bp.rs:157:42
    |
157 |         let mut lines: [heapless::String<32>; 4] = [
    |                                          ^^

warning: unused import: `core::fmt::Write`
  --> examples/ads1015-adc-display-bp.rs:68:5
   |
68 | use core::fmt::Write;
   |     ^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default

error: aborting due to previous error; 1 warning emitted

examples on other HAL crates

Thank you @eldruin for your work on the device crates. As a novice I also very much appreciate your examples. Learning Rust I have been working through many examples and it is really nice to find well document ones that do not require out of date crates.

While you are well ahead of me in almost all respects, I have found a small innovation to organize example code that I have worked on with different HALs, and I hope you may find it interesting. The innovation is simply to organize the HAL/hardware specific code into a setup() function and return the objects used for the application. This allows the actual application code to be generic. Doing this I can build most of your bluepill examples using boards bluepill (stm32f103), discovery-stm32f303 , blackpill-stm32f401, blackpill-stm32f411, and some others. 22 of 25 examples I have tried are building.

The main complication of the setup functions is that the return type needs to be specified. This is greatly simplified if impl trait can be used. For many of your examples I need to return only i2c, led, and delay. Based on work I had already done it was mostly a simple copy and paste operation to get these working. I am having trouble with examples ad9833-midi-player, si4703-fm-radio-display, si4703-fm-radio, iaq-core-c-gas-voc-usart-logger, and ccs811-gas-voc-usart-logger as follows:

  • for ad9833-midi-player I need a trait for the object returned by Ad983x::new_ad9833(spi, cs). At least, this seems preferred based on other spi examples I have worked with. If there is a trait for this then I have not found it.

  • for si4703-fm-radio-display and si4703-fm-radio the type mismatch / trait problems reported in #2 has me stuck.

  • iaq-core-c-gas-voc-usart-logger-bp and ccs811-gas-voc-usart-logger-bp have no entry point and call external C code. I do not understand this and have not attempted these examples.

I have also made these small changes:

  • my workflow CI uses build with example xxx for each example xxx. I have never had luck with just building examples. I don't see how it can actual build the examples because it does not require setting a feature for the MCU, so I think it just does some general checks.

  • blink is converted to be a method for an LED trait. This also looks after on being high with some hardware and low with other hardware.

  • in max30102-heart-usart the writeln!(tx, ...) is converted to block!(tx.write(... )) and u32 to u8. This is because writeln! is not supported by all HAL crates. I am not sure if u8 vs u32 is a problem.

  • I have been working with an object for the radio controls as reported in #2.

Also,

  • I am using github versions of many crates because of changing naming conventions (eg pac vs stm32) and some release versions being a bit old.

  • I do not yet have hardware to run much of this, it is only build tested.

  • RUSTFLAGS: '-D warnings' stops the CI on warning, which prevents testing any following examples, so I have removed it. Warnings do get reported in the workflow artifacts.

  • I'm not sure if your scripts are used in ci-rpi and microbit testing, I think they may be mostly left over from Travis. I am not using them.

  • I'm not sure if DRIVERS are used in ci-rpi and microbit testing. I have not removed it but am not using it.

I have used HALS stm32f0xx, stm32f1xx, stm32f3xx, stm32f4xx, stm32h7xx, stm32l0xx, stm32l1xx, and stm32l4xx. All are working except stm32f0xx and stm32l0xx which are reporting

error[E0599]: no method named `compare_exchange` found for reference `&AtomicBool` in the current scope
   --> /home/paul/.cargo/registry/src/github.com-1ecc6299db9ec823/shared-bus-rtic-0.2.2/src/lib.rs:189:16
    |
189 |         atomic.compare_exchange(current, new, success, failure)
    |                ^^^^^^^^^^^^^^^^ method not found in `&AtomicBool`

error: aborting due to previous error

The code is at https://github.com/pdgilbert/driver-examples branch generic-bare-metal, and the CI is at https://github.com/pdgilbert/driver-examples/actions. It is still work in progress so the most recent workflow results may not be the most representative.

If there is interest in this I will work it into a PR when I have remaining examples working.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.