Closed nikhilkalige closed 6 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 RegisterBlock
s 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.
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
)
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 (const
s), 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
.
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.
I am trying to make use of the blue-pill crate with my own
stm32f411
svd generated crate, instead ofstm32f103xx
.But when I try to compile I get this error,
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 shouldTIM3
be accessingtim2::registerblock
?Can you please help understand?