japaric / stm32f103xx-hal

HAL for the STM32F103xx family of microcontrollers
Apache License 2.0
115 stars 40 forks source link

using with different micro-controller crate #18

Closed nikhilkalige closed 6 years ago

nikhilkalige commented 7 years ago

I am trying to make use of the blue-pill crate with my own stm32f411 svd generated crate, instead of stm32f103xx.

But when I try to compile I get this error,

error[E0271]: type mismatch resolving `<stm32f411::TIM3 as core::ops::Deref>::Target == stm32f411::tim2::RegisterBlock`
  --> /home/lonewolf/workspace/embedded/bikepov/bsp/src/timer.rs:34:13
   |
34 | unsafe impl TIM for TIM3 {
   |             ^^^ expected struct `stm32f411::tim3::RegisterBlock`, found struct `stm32f411::tim2::RegisterBlock`
   |
   = note: expected type `stm32f411::tim3::RegisterBlock`
              found type `stm32f411::tim2::RegisterBlock`
   = note: required by `timer::TIM`

I am a newbie in rust, so please forgive any silly questions. I find it hard to understand as to how this compiles for the 2wd crate. Why should TIM3 be accessing tim2::registerblock?

Can you please help understand?

japaric commented 7 years ago

I am trying to make use of the blue-pill crate with my own stm32f411 svd generated crate

Just to let you know: that's going to require quite a few changes because the registers of e.g. GPIOA of f1 devices are not exactly the same as of f4 devices. Notably alternate function selection on f4 devices is different (simpler, no AFIO peripherals).

I am a newbie in rust, so please forgive any silly questions. I find it hard to understand as to how this compiles for the 2wd crate. Why should TIM3 be accessing tim2::registerblock?

I suggest you check the documentation on generics and the newtype pattern first but here it goes:

On the stm32f103 microcontroller the timers TIM2, TIM3 and TIM4 have the exact same functionality and registers; they are "general purpose timers" -- that's how the reference manual calls them.

For reasons related to generic programming we want each of these general purpose timers to have different types in rust code: TIM2, TIM3 and TIM4 (for example: you may want to write an API that only works with TIM2 but not with TIM3). However we also want to be able to use these 3 types as if they were the same type because they provide the same functionality. To do that we use the newtype pattern:

struct Common;
impl Common { /* */ }
struct A(Common);
struct B(Common);
struct C(Common);

We do something like that ^. In the case of the timers the common type is tim2::RegisterBlock. TIM2, TIM3 and TIM4 would be A, B and C. Now tim2::RegisterBlock has all the public API; to make that API available on TIM2 et al. we implement the Deref trait for TIM2 and the others like this:

impl Deref for TIM2 {
    type Target = tim2::RegisterBlock;

    fn deref(&self) -> &tim2::RegisterBlock {
        &self.0
    }
}

With this you can get a tim2::RegisterBlock type from TIM2 and access its API:

fn foo(tim2: &TIM2) {
    let register_block: &tim2::RegisterBlock = tim2.deref();
    register_block.some_api(..);
}

You can do the same with TIM3 and TIM4. Now Deref is a bit magic because it can happen impliticly such that tim2.some_api(..) is actually the same as tim2.deref().some_api(..).

That should explain why TIM3 refers to tim2::RegisterBlock. The reason why the TIM trait exists is to abstract over TIM1, TIM2, TIM3 and TIM4. You see the TIM1 timer is different; it's an "advanced timer" and has different registers than general purpose timers like TIM2 so TIM1 doesn't derefs to tim2::RegisterBlock but rather to tim1::RegisterBlock -- these two RegisterBlocks are different types.

As for your particular error: I suspect that on your device crate TIM2, TIM3 and TIM4 are newtypes over tim3::RegisterBlock and that tim2::RegisterBlock doesn't exist but probably both stm32f103xx::tim2::RegisterBlock and stm32f411::tim3::RegisterBlock are semantically the same thing. Why that turned out to be like that? Probably because the peripherals were listed in different order in your SVD file.

nikhilkalige commented 7 years ago

Thanks a lot for the explanation, I have been struggling to get a hold of the concepts for a long time now.

I did make changes to the GPIO api's to reflect the functional references. I will try to change from tim2 to tim3 and see if that compiles.

I hope I am understanding this right, so even if you *tim3 it results in accessing tim2.register_block right? So is there a way of saying access your own register_block rather than the default (as in this case it's tim2)

japaric commented 7 years ago

I hope I am understanding this right, so even if you *tim3 it results in accessing tim2.register_block right?

&*tim3 which is equivalent to tim3.deref() has type &tim2::RegisterBlock. tim2 is a module and RegisterBlock is a struct, a type. The types and modules look like this:

pub mod tim2 {
    pub struct RegisterBlock {
        pub cr1: CR1,
        // ..
    }
}

pub struct TIM2(tim2::RegisterBlock);
pub struct TIM3(tim2::RegisterBlock);
pub struct TIM4(tim2::RegisterBlock);

I should note that struct Foo { x: u8 } is equivalent to struct Foo(u8); in the latter case the field has no name but you can still refer to it as foo.0 (where foo: Foo) -- in the former case you refer to the field as foo.x.

So is there a way of saying access your own register_block rather than the default (as in this case it's tim2)

Note that up to this point we have only discussed types, not values.

Apart from the types TIM2 et al. there are also constants (consts), values, that look like this:

pub const TIM2: Peripheral<TIM2> = Peripheral::new(0x40000000);
pub const TIM3: Peripheral<TIM3> = Peripheral::new(0x40000400);
pub const TIM4: Peripheral<TIM3> = Peripheral::new(0x40000800);

You can think of the type Peripheral<TIM2> as *mut TIM2. TIM2.deref() (here TIM2 is the const value) has type &tim2::RegisterBlock and TIM3.deref() has the same type: &tim2::RegisterBlock but TIM2.deref() is the address 0x40000000 and TIM3.deref() is the address 0x40000400. This is a bit like how x and y may both have type u8 but x has value 42 and y has value 24.

ChristianRinn commented 7 years ago

For the STM32F411 the timers 2,3,4 and 5 are not identical, since the timers 2 and 5 are 32bit timers while 3 and 4 are 16bit timers. As far as I can tell the generated code from the .svd file represents that information.

To give you a clearer idea where the error originates from, it is useful to have a look into the SVD files themselves. E.g. for the STM32F103 you have something like

    <peripheral>
      <name>TIM2</name>
      <description>General purpose timer</description>
      <groupName>TIM</groupName>
      <baseAddress>0x40000000</baseAddress>
      <addressBlock>
        <offset>0x0</offset>
        <size>0x400</size>
        <usage>registers</usage>
      </addressBlock>
      <interrupt>
        <name>TIM2</name>
        <description>TIM2 global interrupt</description>
        <value>28</value>
      </interrupt>
      <registers>
        <register>
          <name>CR1</name>
          <displayName>CR1</displayName>
          <description>control register 1</description>
          <addressOffset>0x0</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x0000</resetValue>
          <fields>
            <field>
              <name>CKD</name>
              <description>Clock division</description>
              <bitOffset>8</bitOffset>
              <bitWidth>2</bitWidth>
            </field>
            <field>
              <name>ARPE</name>
              <description>Auto-reload preload enable</description>
              <bitOffset>7</bitOffset>
              <bitWidth>1</bitWidth>
            </field>
            .....

Which is basically the description of all the registers with all their fields of the particular instance of Timer 2. For the STM32F103 the timers 2,3,4 and 5 are identical regarding these registers and fields, which is why the timers 3,4 and 5 are simply described with

    <peripheral derivedFrom="TIM2">
      <name>TIM3</name>
      <baseAddress>0x40000400</baseAddress>
      <interrupt>
        <name>TIM3</name>
        <description>TIM3 global interrupt</description>
        <value>29</value>
      </interrupt>
    </peripheral>
    <peripheral derivedFrom="TIM2">
      <name>TIM4</name>
      <baseAddress>0x40000800</baseAddress>
      <interrupt>
        <name>TIM4</name>
        <description>TIM4 global interrupt</description>
        <value>30</value>
      </interrupt>
    </peripheral>
    <peripheral derivedFrom="TIM2">
      <name>TIM5</name>
      <baseAddress>0x40000C00</baseAddress>
      <interrupt>
        <name>TIM5</name>
        <description>TIM5 global interrupt</description>
        <value>50</value>
      </interrupt>
    </peripheral>

I.e. the only thing that changes is the base address and the IRQn. As we already know that e.g. TIM3 is derived from TIM2 we can exploit that within the generated code and make it more generic which is what japaric described above.

If you look at the .svd file for the STM32F411 you can see that TIM3 is not derived from TIM2.

    <peripheral>
      <name>TIM2</name>
      <description>General purpose timers</description>
      <groupName>TIM</groupName>
      <baseAddress>0x40000000</baseAddress>
      <addressBlock>
        <offset>0x0</offset>
        <size>0x400</size>
        <usage>registers</usage>
      </addressBlock>
      <interrupt>
        <name>SPI3</name>
        <description>SPI3 global interrupt</description>
        <value>51</value>
      </interrupt>
      <registers>
        <register>
          <name>CR1</name>
          <displayName>CR1</displayName>
          <description>control register 1</description>
          <addressOffset>0x0</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x0000</resetValue>
          <fields>
            <field>
              <name>CKD</name>
              <description>Clock division</description>
              <bitOffset>8</bitOffset>
              <bitWidth>2</bitWidth>
            </field>
           .......
           .......

    <peripheral>
      <name>TIM3</name>
      <description>General purpose timers</description>
      <groupName>TIM</groupName>
      <baseAddress>0x40000400</baseAddress>
      <addressBlock>
        <offset>0x0</offset>
        <size>0x400</size>
        <usage>registers</usage>
      </addressBlock>
      <interrupt>
        <name>SPI4</name>
        <description>SPI4 global interrupt</description>
        <value>84</value>
      </interrupt>
      <registers>
        <register>
          <name>CR1</name>

If you are interested I would be willing to help out with the crate for the Nucleo F411 because I have one myself and I think that quite a lot of others do, probably because it provides a lot of memory and is quite fast in regard to its price.

BTW: I do not really understand why the interrupt declared in the TIM2 peripheral is the SPI3 global interrupt, while the TIM2 global interrupt is declared in the GPIOA peripheral. Anyhow this is a bit off topic here.