Author Topic: STM32 macro for port and pin read and write  (Read 14845 times)

0 Members and 1 Guest are viewing this topic.

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: STM32 macro for port and pin read and write
« Reply #25 on: December 19, 2019, 01:25:59 pm »
>Hmm .. embedded is mostly the domain of hardware guys, writing firmware using -O0 as a compiler option.
Do not expect applause here ;)

Have no idea what you are talking about, I use nothing but -Os. I'm not sure what domain I'm in, or what domain I'm supposed to be in, but I seem to be dealing with hardware and software and probably am in no single category.
The links should already have that info-
-Os -mcpu=cortex-m0plus -std=c++14

Another variation, where the pin properties can be used in the constructor, and in any order or not at all-
https://godbolt.org/z/Xs39xD

Gpio<PC6, LOWISON> led(SPEEDHI, OUTPUT);
Gpio<PA0, LOWISON> sw1(INPUT, PULLUP);
Gpio<PA1> led2(OUTPUT);
Gpio<PA2> analog1;
Gpio<PA3, LOWISON> pump(ODRAIN, PULLUP, OUTPUT);

The 2 template parameters need to remain as template parameters (second has a default value) as that info is needed in the functions (the template parameters are carried around wherever the var name goes, so no storage of that info is needed).

If you miss all the macro work, in c++ you can replace all that fun with a different fun- templates.


...and another variation, where only one template parameter needed-
https://godbolt.org/z/JVJVXy

the pin is the only template parameter, and each pin has an alternate name with L added to it for LOWISON-
Gpio<PC6L> led(SPEEDHI, OUTPUT);
Gpio<PA0L> sw1(INPUT, PULLUP);
Gpio<PA1> led2(OUTPUT);
Gpio<PA2> analog1;
Gpio<PA3L> pump(ODRAIN, PULLUP, OUTPUT);

The LOWISON is needed for the on/off/isOn/isOff functions, so you do not need to keep track of what value is on/off. When created, you specify whether low is on or not, and when used you just need to do on()/off() and no longer need to know what on/off actual values are-

led.on(); //whether that is high/low, don't need to know as it was already specified at 'led' creation (PC6L)

« Last Edit: December 20, 2019, 02:09:09 am by cv007 »
 
The following users thanked this post: rvalente, emece67

Online rvalenteTopic starter

  • Frequent Contributor
  • **
  • Posts: 726
  • Country: br
Re: STM32 macro for port and pin read and write
« Reply #26 on: December 19, 2019, 02:10:34 pm »
Tks guy, you helped me a lot!
 

Offline twiddle

  • Contributor
  • Posts: 19
  • Country: au
Re: STM32 macro for port and pin read and write
« Reply #27 on: December 20, 2019, 10:55:12 pm »
Looking at svdconv, there is a possibility to generate a header in various styles from a svd file. I wonder if anyone has tried to generate some sensible c++ out of a svd file?

I use python and the cmsis-svd library to generate cppreg-based headers from the svds.

Generated headers look like
Code: [Select]
struct AES {
using AES_pack = cppreg::RegisterPack<0x50060000,0x400>;

//control register
struct CR : cppreg::PackedRegister<AES_pack, cppreg::RegBitSize::b32, 8 *0x0> {
using DMAOUTEN = cppreg::Field<CR,1,0xc,cppreg::read_write>; //Enable DMA management of data output phase
using DMAINEN = cppreg::Field<CR,1,0xb,cppreg::read_write>; //Enable DMA management of data input phase
using ERRIE = cppreg::Field<CR,1,0xa,cppreg::read_write>; //Error interrupt enable
using CCFIE = cppreg::Field<CR,1,0x9,cppreg::read_write>; //CCF flag interrupt enable
using ERRC = cppreg::Field<CR,1,0x8,cppreg::read_write>; //Error clear
using CCFC = cppreg::Field<CR,1,0x7,cppreg::read_write>; //Computation Complete Flag Clear
using CHMOD = cppreg::Field<CR,2,0x5,cppreg::read_write>; //AES chaining mode
using MODE = cppreg::Field<CR,2,0x3,cppreg::read_write>; //AES operating mode
using DATATYPE = cppreg::Field<CR,2,0x1,cppreg::read_write>; //Data type selection
using EN = cppreg::Field<CR,1,0x0,cppreg::read_write>; //AES enable
};
<snip>
};
Driver code looks like :
Code: [Select]
template <typename Peripheral>
class TADC
{
    public:
   
    static void Enable()
    {
        RCC::APB2ENR::ADCEN::set();
        Peripheral::CR::ADEN::set();
        //wait for adrdy flag
    }

    static void Disable()
    {
        Peripheral::CR::ADDIS::set();
        //wait for ADDIS to clear
    }
    static void Start()
    {
        Peripheral::CR::ADSTART::set();
    }

application code:
Code: [Select]
        TADC<ADC>::EnableChannel<15>();
        TADC<ADC>::Enable();
        TADC<ADC>::SetSampleTime(FieldValues<ADC::SMPR::Data>::SAMPLETIME_19CYCLE5);
        TADC<ADC>::Calibrate();

Possibly more verbose than people like, and I know everybody likes to hate on templates (many likely wouldn't consider this *sensible* C++  :-DD), but the generated assembly is equal to or better than the CMSIS equivalent (some performance comparisons are  @ https://github.com/sendyne/cppreg/blob/master/Performance.md) and I gain the benefits of more compile-time verification, compile-time enforcement of readonly/writeonly registers, the ability to merge a number of writes into a single operation, etc etc. 

Of course the issue here is that different vendors tend to mess their svd files up in various ways. NXP like to leave out the core registers and only include their peripherals, for example, there's sometimes fields missing in STM svds. So this approach is only as good as the SVD files.
For the smaller projects I work on, where I have fun trying to make the compiler do as much verification and testing/checking for me as I can, it's been very good, and allows me to avoid having to jump through hoops with C linkage for my interrupt handlers etc (with my framework, there's no dependency on CMSIS headers and the reset handler is the only thing requiring C linkage in the entire project).
 
The following users thanked this post: hans, thm_w, JohnnyBerg

Offline JohnnyBerg

  • Frequent Contributor
  • **
  • Posts: 474
  • Country: de
Re: STM32 macro for port and pin read and write
« Reply #28 on: December 21, 2019, 11:18:29 am »

For the smaller projects I work on, where I have fun trying to make the compiler do as much verification and testing/checking for me as I can, it's been very good, and allows me to avoid having to jump through hoops with C linkage for my interrupt handlers etc (with my framework, there's no dependency on CMSIS headers and the reset handler is the only thing requiring C linkage in the entire project).

Very nice! I'm trying to put some c++ together to do the same thing. When presentable, I'll put it on github.

I've been struggling with the interrupt vectors, but did not find a viable solution. So I am still jumping though C hoops. Any pointers in the right direction?
 

Offline twiddle

  • Contributor
  • Posts: 19
  • Country: au
Re: STM32 macro for port and pin read and write
« Reply #29 on: December 21, 2019, 08:09:12 pm »
It depends on which bit you're struggling with - if you're having trouble placing your table at the right location you can use attributes to forcibly place it in a 'vector table' section defined in your linker script.
Once you've got it in the right spot its just a matter of doing whatever you want to construct a contiguous array (so use a C array or std::array) containing the function pointers for your table.
 

Offline taemun

  • Regular Contributor
  • *
  • Posts: 110
  • Country: au
Re: STM32 macro for port and pin read and write
« Reply #30 on: July 30, 2020, 05:31:13 am »
If someone else finds this on Google in the future, here's a half-step solution to the problem, allowing reuse of the STM32Cube provided functions and auto-generated variables:

Code: [Select]
#define OUT_LOW(var)      HAL_GPIO_WritePin(var##_GPIO_Port, var##_Pin, 0)
#define OUT_HIGH(var)     HAL_GPIO_WritePin(var##_GPIO_Port, var##_Pin, 1)
#define IN(var)           HAL_GPIO_ReadPin(var##_GPIO_Port, var##_Pin)
#define OUT_TOGGLE(var)   HAL_GPIO_TogglePin(var##_GPIO_Port, var##_Pin)

Can be used like so:

Code: [Select]
OUT_HIGH(ADC_START); // start conversion
while (IN(ADC_DRDY)) {} // wait until conversion is complete
OUT_LOW(ADC_START);
 

Offline grantb5

  • Regular Contributor
  • *
  • Posts: 139
  • Country: ca
Re: STM32 macro for port and pin read and write
« Reply #31 on: August 09, 2020, 10:41:05 pm »
Another noob here just curious about your thoughts of doing it like below. I'm just working through a book that uses the demo version of Keil. In the before time, like the OP, my statement would look like this (8051 C code):

Code: [Select]
sbit pLCD_E  = P1^7;                   // [OUT] LCD active-high enable.
   :
pLCD_E = 1;                         // E high.
pLCD_E = 0;                         // E low again.

STM32: So I don't know if "inline" buys me anything with Keil C (not C++), but here is a function that's kind of equivalent. I'm not sure how to do this in a macro.

Code: [Select]
#define  LCD_CTRLPORT   GPIOB          // LCD control pins on Port B
#define  LCD_EN  5
   :   
static void pLCD_E(uint32_t lvl)
{
LCD_CTRLPORT->BSRR = ((uint32_t)1<<LCD_EN) << (16 * !lvl);
}
   :
pLCD_E(1);
pLCD_E(0);




 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: STM32 macro for port and pin read and write
« Reply #32 on: August 09, 2020, 10:57:20 pm »
With inline there is a chance that compiler would actually optimize things. Since you are calling it with constants as arguments, it is way better to have it inlined  so there will be a trivial amount of code inlined for each possible argument value. In case if function is not inlined, compiler may assume that lvl can be any value, not just 0 or 1, and will generate an actual multiplication code.

Also, it is better to just have two version - one for set and one for clear. It makes no difference for the code, but avoids all the math. And in that case just use BOP and BC instead of BSRR.

Also, seriously look at my macros, they are really good for this stuff.
Alex
 

Offline grantb5

  • Regular Contributor
  • *
  • Posts: 139
  • Country: ca
Re: STM32 macro for port and pin read and write
« Reply #33 on: August 09, 2020, 11:39:41 pm »
Thanks. That's interesting. I was having a look at stdbool.h but didn't investigate what that would do yet, if it would even let me.

The assembly code generated varies remarkably depending on the optimization.  The implementation is definitely more bizarre than I expected. Almost thinking that an assembler module or function might be better, but with the speed and code size beyond what I'm used to, it would only be a mental exercise.

Edit: A multiplication would only add,what, a cycle? Hard to wrap my head around just leaving things as 32-bit where before, even 8 would be a waste.
« Last Edit: August 09, 2020, 11:41:31 pm by grantb5 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11238
  • Country: us
    • Personal site
Re: STM32 macro for port and pin read and write
« Reply #34 on: August 09, 2020, 11:41:30 pm »
stdbool.h will not do anything useful here.

There is no need for assembly, just don't interfere with the compiler's ability to optimize and you will get code as optimal as anything you can write by hand.

Alex
 

Offline grantb5

  • Regular Contributor
  • *
  • Posts: 139
  • Country: ca
Re: STM32 macro for port and pin read and write
« Reply #35 on: August 14, 2020, 12:14:59 am »
It took a while to find a function or macro that Keil wouldn't turn into something crazy. Turns out that simplest was best. I like to have my bit-banging determinable and symmetric where possible. This worked out really well for just setting or clearing a pin. I have the optimization off and will probably leave it that way, at least for this module.

Code: [Select]
#define  pLCD_E(lvl)   LCD_CTRLPORT->BSRR = ((uint32_t)1<<LCD_EN) << (16 * !lvl)

It basically loads the constant for the BSRR address in a register and the constant for the bit in another, and Bob's your uncle. The only variation in execution speed is just if that address is already in a register. (And yes, interrupts are/will be OK).

Putting it in a function or other cleverness just messes it up. Now, as I expected, I need NOP's to meet the timing requirements. The 48MHz 8051 needed them, so when I saw my STM32 sucking horribly speed-wise I knew that something wasn't right.

Thanks for help and encouragement. I've never wasted so much time on a couple of pins haha. The abstraction of the libraries might be OK. Now that I understand what's possible I can evaluate the trade-offs.
« Last Edit: August 14, 2020, 12:18:02 am by grantb5 »
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: STM32 macro for port and pin read and write
« Reply #36 on: August 14, 2020, 04:32:00 am »
You may want to note that the BRR register has a purpose. When using a pin bitmask to set a pin via BSRR, its counterpart to clear a pin (BRR) can use the same bitmask (assuming setting/clearing are relatively close to each other in the code). The instruction set will favor using an offset register rather than getting bits into the upper bytes, so having the same bitmask can be an advantage.

You can compare asm output so which is 'better'-
https://godbolt.org/z/nne4xE

Not much of a game changer, but it doesn't hurt to realize there can be a better option. Maybe not all stm32 have both registers but I would suspect most do.
 

Offline grantb5

  • Regular Contributor
  • *
  • Posts: 139
  • Country: ca
Re: STM32 macro for port and pin read and write
« Reply #37 on: August 14, 2020, 01:29:49 pm »
Thanks, that's an interesting site! I will definitely dig deeper there.

The "Blue Pill" STM32F103 does have the BRR. I am using Keil for now. I'm just learning this MPU, so I took the easy route for toolchain installation (on Windows). Someone also pointed out that part of the Peripheral space is bit addressable too ("bit-banding" -  how I hate that term).

I am working through the book, The STM32F103 Arm Microcontroller and Embedded Systems: Using Assembly and C, and the LCD chapter was the first one that had any meaningful code for real life. Previously it was purely educational stuff. I didn't really care for the solution in the textbook, so I branched out. I do recommend this book so far. You have to start someplace and this is decent.


 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf