rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
286 stars 68 forks source link

display + IRQ/timer example proposal #109

Closed nebelgrau77 closed 3 years ago

nebelgrau77 commented 4 years ago

SSD1306 and interrupts/timers example

I believe it would be useful to add an example with the SSD1306 being used to visualize something that is updated with interrupts, e.g. a counter, timer or similar. As the F0xx crate specifically requires the critical sections to be used in peripherals setup, it is not easy to adapt existing examples made for F1 or F4 boards. I tried to re-use and adapt the code for blinking LEDs with interrupts from the F0xx-HAL crate, but I don't know how to approach it. In case of LEDs it seems quite straightforward: they are made global variables, wrapped in Mutex. How should this be done for the display: is it the SDA and SCL pins, as well as I2C and RCC that have to be global, or is there a way to make the display instance global, while everything else is set up within the Critical Section? If the latter is true, what should the type be, and what needs to be inside mutex?

Here's my non-working code that gives an idea of what the problem is. https://github.com/nebelgrau77/STM32F0-SSD1306-IRQ_DRAFT

This is a working example on STM32F411: it is easier with that crate, as the peripherals don't need to be defined within CS: https://github.com/nebelgrau77/STM32F4-OLED-IRQ_timecounter

I believe such example would be useful for any kind of project that needs to use interrupts AND a display, e.g. involving some ADC readings, clocks and such.

jamwaffles commented 4 years ago

I believe it would be useful to add an example with the SSD1306 being used to visualize something that is updated with interrupts

Definitely! jamwaffles/embedded-graphics#150 has some demo ideas in it. I like the idea of a "DVD player screensaver", but an example (probably using RTFM) that handles timers and animation of anything would be good for copypasta.

everything else is set up within the Critical Section?

Do you mean this line? If so, why not move the loop inside the if? You could also use unwrap():

let mut p = Peripherals::take().unwrap();
let mut cp = c_m_Peripherals::take().unwrap();

I'm not entirely sure what you're asking here.

If the latter is true, what should the type be, and what needs to be inside mutex?

The type of the display will be huge. You should make a type alias similar to this:

type Display = ssd1306::mode::graphics::GraphicsMode<
    ssd1306::interface::i2c::I2cInterface<
        stm32f1xx_hal::i2c::BlockingI2c<
            I2C2,
            (
                gpio::gpiob::PB10<gpio::Alternate<gpio::OpenDrain>>,
                gpio::gpiob::PB11<gpio::Alternate<gpio::OpenDrain>>,
            ),
        >,
    >,
>;

The display owns SCK/SCL so you can store disp a single mutex.

nebelgrau77 commented 4 years ago

everything else is set up within the Critical Section?

Do you mean this line? If so, why not move the loop inside the if?

That will prevent the interrupts from firing, I ran into this problem before. A loop put in the critical section will make it stay in CS forever, hence no interrupts, which I need for the code to work.

You could also use unwrap():

let mut p = Peripherals::take().unwrap();
let mut cp = c_m_Peripherals::take().unwrap();

I admit I don't understand how this solves the problem: is it supposed to replace the if let (...)?

I'm not entirely sure what you're asking here.

If the latter is true, what should the type be, and what needs to be inside mutex?

The type of the display will be huge. You should make a type alias similar to this:

type Display = ssd1306::mode::graphics::GraphicsMode<
    ssd1306::interface::i2c::I2cInterface<
        stm32f1xx_hal::i2c::BlockingI2c<
            I2C2,
            (
                gpio::gpiob::PB10<gpio::Alternate<gpio::OpenDrain>>,
                gpio::gpiob::PB11<gpio::Alternate<gpio::OpenDrain>>,
            ),
        >,
    >,
>;

The display owns SCK/SCL so you can store disp a single mutex.

Yep, that's what I'm looking for, let's see if it works :)

therealprof commented 4 years ago

@nebelgrau77

That will prevent the interrupts from firing, I ran into this problem before. A loop put in the critical section will make it stay in CS forever, hence no interrupts, which I need for the code to work.

I don't really the connection between the way the initialisation works in that HAL and this crate. There's nothing special about it really.

nebelgrau77 commented 4 years ago

I'm sure there's a lot I don't understand and that's why I can't figure this out :) but:

STM32F4xx-HAL:

pub fn into_push_pull_output(self) -> PA0<Output<PushPull>>

STM32F0xx-HAL:

pub fn into_push_pull_output( self, _cs: &CriticalSection) -> PA0<Output<PushPull>>

The way I understand it, the F0 requires CS to initialize peripherals. If that is done as in the examples, in an if let {} block, I either have to make the display available outside of that block, or cannot use the loop as it will be within the Critical Section and prevent interrupts from firing.

But I admit, I might have missed something :)

therealprof commented 4 years ago

Yes, but what exactly does this have to do with this crate? If you look at non-STM microcontrollers initialisation going to be different again.

Simply limit the CS to strictly the places where you need it and everything is going to be fine. You can pass any value out of a CS and usually if you want to share resources you'll need to use a static Mutex which also requires a CS to put values into (or to get them out of).

Getting acquainted with critical sections is key to doing anything with interrupts, regardless of what you're doing with them. If you want to supply a working and unique example in a PR that might be considered for inclusion, though at some point we may have to start a separate ssd1306-examples crate and limit this crate to purely essential getting-started examples and CI tests.

nebelgrau77 commented 4 years ago

OK, I see what you mean. It should be an example for the STM32F0 crate, not for the SSD1306. Gotcha.