modelica-3rdparty / Modelica_DeviceDrivers

Free library for interfacing hardware drivers to Modelica models. There is support for joysticks, keyboards, UDP, TCP/IP, LCM, MQTT, shared memory, AD/DA converters, serial port and other devices.
BSD 3-Clause "New" or "Revised" License
76 stars 30 forks source link

STM32F4 support #218

Open bernhard-thiele opened 7 years ago

bernhard-thiele commented 7 years ago

PR #216 from @it-cosmos provided support for STM32F4 which I merged into an STM32F4-feature branch (ae66aeff11a3681057c0d7fa56b76097c27b9d7d).

This ticket shall serve as a point for discussing this feature with the aim to finally merge the feature into the master branch (probably also interesting for @sjoelund who provided the AVR support in #170).

it-cosmos commented 7 years ago

Can the makefile for compiling and creationg the binaries in Modelica_DeviceDrivers/EmbeddedTargets/STM32F4/Examples/STM32F4_Discovery/Blink/Makefile remain on this location? Here also the blink_main.mos is checked in in this directory. Is this in line with the guide lines? with the Makefile a Debug directory is created with all object code. Should the location be changed?

bernhard-thiele commented 7 years ago

I just looked a bit more into the code. You moved the third party code into Modelica_DeviceDrivers/3rdParty/STM, however the designated folder for third party code is Modelica_DeviceDrivers/Resources/thirdParty/. Please move the STM code below that directory.

If you have a look into the Resources/thirdParty folder, you can see that I listed the third party code in the Readme.txt. It's important to keep track of the licenses used for the thirdParty code so that the library can be used and shipped safely with (commercial) products. Maybe this could be even done a bit more clear than it is now.

Conventionally, all non Modelica files in a Modelica library are kept below the Resources directory. Hence, also the blink_main.mos and the Makefile should go there. I should play with it myself to get a feeling of what works best, but the mos script should probably go below a folder Resources/Scripts/OpenModelica, e.g., Resources/Scripts/OpenModelica/EmbeddedTargets/STM32F4/Examples/STM32F_Discovery/ so that the location reflects the concerned model. The Makefile could go into the same directory as the mos file.

Notice that you can easily reference files in the resources directory from the documentation by a construct similar to <a href="modelica://Modelica_DeviceDrivers/Resources/Scripts/MyFile.xxx">MyFile</a>. This could be useful to reference from the documentation to the needed scripts or makefiles.

I maybe would rather create a directory named build instead of Debug. However, since the Makefile actually creates debug object files the name debug fits, too. I have no strong opinion on that.

it-cosmos commented 7 years ago
bernhard-thiele commented 7 years ago

Yes, sounds good.

it-cosmos commented 7 years ago

Shall we add the STMCube32F4 HAL interface into the thirdparty directory? I see no conflict in the license: http://www.st.com/content/ccc/resource/legal/legal_agreement/license_agreement/de/fc/f7/32/a0/8b/4f/db/ultimate-liberty-v2.txt/files/ultimate-liberty-v2.txt/jcr:content/translations/en.ultimate-liberty-v2.txt The size is quite big, so it could be provided as zip file.

bernhard-thiele commented 7 years ago

Well, the license seems okay. However, the zip file has almost 400MB and having a link to it in the documentation (as you currently have) seems to be a good way to handle it. It should not go into the thirdparty directory.

it-cosmos commented 7 years ago

Ok, then only work left is to update the "README" file in the thirdParty folder.

Onother question: I have a look onto the GPIO_EXTI example provided in the STM32F4Cube HAL. I would like to provide this in modelica. In this example the user interrupt is used triggered by the blue user button on the board. On the falling edge of the user button the user LED's are toggled in a callback function. In modelica, is see the only chance to provide the callback function as c-code, which the modelica user has to complete in c-code. As far as I know there is no export facility from modelica to c-function. Any suggestions are welcome.

bernhard-thiele commented 7 years ago

What would be nice, is having a block in which one can configure the external interrupt to use and use that interrupt to activate certain parts of the model. However, there is no direct mapping from Modelica elements to this behaviour, yet. It would be an interesting extension and I would like to see it prototyped in OMC.

For now, I see no nice way how to handle this. It might be possible to sort of use the generated "fmi2DoStep" as a callback function for the ISR, but that would entail some manual C code modifications by the user.

bernhard-thiele commented 7 years ago

Some comments to the build process.

I tried to build the example on my Ubuntu 14.04.5 LTS. It turned out that the cross-compiler packages for that release are not working correctly. This is related to https://stackoverflow.com/questions/26931979/gnu-arm-nano-specs-not-found in which I followed the last suggestion of installing the package gcc-arm-embedded which can be added by an PPA as described at http://yottadocs.mbed.com/#linux-cross-compile. After doing that, I could build the blink project. Today, I ordered the STM32F4 discovery board and I'm looking forward to testing things once the board is delivered.

It would be good to note the Ubuntu version which is the basis for the documentation in the documentation.

it-cosmos commented 7 years ago

Sorry, I'm developing on debian jessy. It was a bit fast to assume ubuntu working in the same way. I will update the documentation for my part. Here I didn't got the problems mentioned. So I can only document developed and tested on debian jessy and problems/solutions/workarounds with ubuntu could be added by ubuntu tester. But for now I can add already mention the problems mentioned.

it-cosmos commented 7 years ago

Regarding Interrupt handling I thought for now to provide a c-template, implementing empty void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin), which is used in the HAL interface, if the corresponding interrupt line is enabled. But before doing so, I would implement simple Digital GPIO read/write and provide an Blink example for that.

bernhard-thiele commented 7 years ago

Anyway, many times Ubuntu and Debian packages work similarly and it is only some small things that differ. I can test from the Ubuntu side and can complement the documentation where needed.

bernhard-thiele commented 7 years ago

@it-cosmos @sjoelund I received the board and could get it working :-)

This is what needs to be done for Ubuntu 14.04.5 LTS (probably this can be improved, but just for not forgetting it):

sudo apt-get install git libusb-1.0.0-dev pkg-config autotools-dev
git clone https://github.com/texane/stlink.git

It seems one needs to install stlink. I tried without it, but I never could connect to the board. Installation and configuration hints are given on the project site, compilation is described in https://github.com/texane/stlink/blob/master/doc/compiling.md and I did basically:

cd stlink
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
make install
udevadm control --reload-rules
udevadm trigger

Now one probably should set up an stlink group like described in the documentation for getting the access rights correct. I didn't do that in this experiment and simply used sudo everywhere:

sudo st-util

Check the number of the opened port (default for me: 4242). I needed to start st-util first before I could connect with gdb:

sudo arm-none-eabi-gdb
# Now in the debugger console enter
target remote localhost:4242
monitor reset halt
file Blink_main.elf
load
monitor reset
continue
it-cosmos commented 7 years ago

I missed that part, I think I got it easy since I started experimenting with TrueStudio lite, where st-link could be easily selected for connection. But I already forgot what I've really done. Good, that it's working now.

it-cosmos commented 7 years ago

I implemented GPIO Read and Write. The example here doesn't work because the Read Block is just initialized to false. The intension is, that when the blue user button is pressed all user leds should blink. In DigitalReadBoolean Block I have: protected Functions.Digital.InitRead digital = Functions.Digital.InitRead(init, port, pin); equation y = Functions.Digital.read(digital, pin); annotation(Icon(graphics = {Text(extent = {{-95, -95}, {95, 95}}, textString =

in BlinkGPIO.mo: connect(User.y, LED6.u) annotation( Line(points = {{-50, 20}, {-10, 20}, {-10, -14}}, color = {255, 0, 255})); the generated code shows: `typedef struct BlinkGPIO_fmi2Component_s { fmi2Real currentTime; fmi2Integer fmi2IntegerVars[1]; fmi2Boolean fmi2BooleanVars[1]; void* extObjs[7]; } BlinkGPIO_fmi2Component;

BlinkGPIO_fmi2Component BlinkGPIO_component = { .fmi2IntegerVars = { 0 /synchronizeRealtime1._tick/, }, .fmi2BooleanVars = { fmi2False /User._y/, }, };`

The problem is that fmi2BooleanVars is intitialised to false and never gets updated with the current value in time the Function.Digital.read is never called in the code. How to solve this?

sjoelund commented 7 years ago

I think you need to sample the read. Impure/external calls should not be performed in a continuous-time equation.

it-cosmos commented 7 years ago

Manually inserting the line comp->fmi2BooleanVars[0] = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_Digital_read(comp, comp->extObjs[9], 1) in the generated code, function BlinkGPIO_functionOutputs,, the example works. Shouldn"t that be generated from the equotion in the protected Functions.Digital.InitRead digital = Functions.Digital.InitRead(init, port, pin); equation y = Functions.Digital.read(digital, pin);?

sjoelund commented 7 years ago

I think in some parts of the backend, it might detect that the function doesn't vary over time and only reads it once. Anyway, the Modelica specification has some restrictions on calling impure functions (that we do not check, but if you don't follow them you might get unexpected results). Perhaps adding the impure keyword (or annotation) to the read function also fixes this (it should be added almost everywhere as a compiler hint).

it-cosmos commented 7 years ago

Unfortunately sample doesn't work yet. With the modification of the equation; equation when sample(0, sampleTime) then y = Functions.Digital.read(digital, pin); end when; I get the output: [CodegenEmbeddedC.tpl:490:28-490:28:writable] Error: Template error: daeExpCallBuiltin: Not supported: sample(1, 0.0, User.sampleTime). [CodegenEmbeddedC.tpl:346:14-346:14:writable] Error: Template error: Unsupported equation: .... "

it-cosmos commented 7 years ago

Right, just adding the impure keyword to the read function fixes the problem. Thanks.

it-cosmos commented 7 years ago

Whith the pull request #221 the GPIO configuration for the speed and pull is hard coded in "MDDSTM32F4Digital.h"; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Pull = @@GPIO_PULLUP;//for output_PP GPIO Mode The speed at least could be made configurable in Modelica with above parameters as default.

it-cosmos commented 6 years ago

I think there is a problem with the omc compiler. Please check https://github.com/it-cosmos/Modelica_DeviceDrivers/tree/STM32F4. In Blink.mo is: Functions.ClockConfig.Init clockConf = Functions.ClockConfig.Init(HALinit, mcu.clock, mcu.pllM, mcu.pllN, mcu.pllP,mcu.pllQ, mcu.ahbPre, mcu.apb1Pre, mcu.apb2Pre, mcu.pwrRegVoltage, mcu.overdrive, mcu.preFlash); Blocks.SynchronizeRealtime synchronizeRealtime1(clockConf = clockConf, timerFrequency = 10000, timerPeriod = 10000) annotation( Placement(visible = true, transformation(origin = {42, 62}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); clocConf is definitly passed to synchronizeRealtime but in the generated code (Blink_main.c) one can see: typedef struct Blink_fmi2Component_s { fmi2Real currentTime; fmi2Boolean fmi2BooleanVars[1]; fmi2Real fmi2RealParameter[1]; void* extObjs[5]; } Blink_fmi2Component; and further: fmi2Status Blink_fmi2EnterInitializationMode(fmi2Component comp) { comp->extObjs[0] /* HALinit EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.HAL.Init */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_HAL_Init_constructor(comp); comp->extObjs[2] /* led._digital EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.Digital.InitLed */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_Digital_InitLed_constructor(comp, comp->extObjs[0] /* HALinit EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.HAL.Init */, 1); comp->extObjs[4] /* led1 EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.Digital.InitLed */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_Digital_InitLed_constructor(comp, comp->extObjs[0] /* HALinit EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.HAL.Init */, 2); comp->extObjs[1] /* clockConf EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.ClockConfig.Init */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_ClockConfig_Init_constructor(comp, comp->extObjs[0] /* HALinit EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.HAL.Init */, 4, 8, 336, 1, 7, 1, 3, 2, 1, comp->fmi2BooleanVars[0] /* mcu._overdrive CONST */, fmi2True); comp->extObjs[6] /* synchronizeRealtime1._sync EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.RealTimeSynchronization.Init */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_RealTimeSynchronization_Init_constructor(comp, comp->extObjs[5] /* synchronizeRealtime1._clockConf EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.ClockConfig.Init */, 10000, 10000); return fmi2OK; }

extObj[5] and [6] is assigned, although only 5 range from 0 to 4 are initialized. Further: As clockconf is passed in the modelica code to synchronizeRealtime1, it is not reflected in the generated code. The corret generated code would be: comp->extObjs[3] /* synchronizeRealtime1._sync EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.RealTimeSynchronization.Init */ = Modelica__DeviceDrivers_EmbeddedTargets_STM32F4_Functions_RealTimeSynchronization_Init_constructor(comp, comp->extObjs[1] /* synchronizeRealtime1._clockConf EXTOBJ: Modelica_DeviceDrivers.EmbeddedTargets.STM32F4.Functions.ClockConfig.Init */, 10000, 10000); return fmi2OK; Please check.