STM32F767 (and simmilar) raw GPIO use - Page 1
Forum section: electronics / general

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 04, 2017, 07:32:04 pm
content: HiI've asked this same thing on the mbed question forum, but I got no solution in two days already.For a project I'm doing I got the Nucleo144 board which has a STM32F767 mcu.I need to have fast access to the GPIO so I'm trying to handle them directly via registers.I've found more than one tutorial on using GPIO via registers for STM but none of them work on the mbed online editor.I just can't find an example or documentation that explains how to get that working for the mcu I have.I just get that the register name is a syntax error, so either its different, or I am missing some library specific for that mcu.

author: FreddyVictor
author_rank: Regular Contributor
timestamp: January 04, 2017, 10:48:00 pm
content: Just a suggestion (as I usually use SPL or HAL) & havn't used mbed !Have you included the correct controller-specific include file ?eg: stm32f767xx.h - this should be included in your project (should already be included with mbed I would have thought ??)then download the reference manualRM0410.pdf- see chapter 6 for GPIO register names

author: DaJMasta
author_rank: Super Contributor
timestamp: January 05, 2017, 06:28:57 am
content: Also haven't used mbed, but using the HAL libraries and the Cube configuration software, it will autogenerate the GPIO pin configurations when building the template initially, and that automatic initialization code in your main program may be able to point you to the registers that are being used and a way to set them.The actual functions used do vary based on the library versions, and they actually vary somewhat between processor series (F1XX will be different from F7XX and L1XX, for example).Often for the basic stuff, just taking a look at any sample programs they supply can help - if you can find your classic hello-world pin blinker program that will compile for your chip, it probably shows you the register and functions you need for that pin in the code.

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 05, 2017, 03:09:50 pm
content: QuoteNot sure, I don't even know which one is it, or where to find it.Including stm32f767xx.h or stm32f7xx.h does nothing.QuoteI have installed cube, but I have no idea how to get those configuration then in the embed online editor.I've saw people using eclipse, but I have yet to figure out how to set it all up.I have mostly been using PIC with MPLABX, where things are much simpler, so I don't even know what I need to look for here.QuoteHello world programs work, but they all use the arduino style functions, in fact all of the code I found uses them, nobody seems to bother with raw port access.There is some library on mbed that is supposed to simulate a parallel bus interface via GPIO but it won't compile since it says mentioned registers are unknown.

author: gperoni
author_rank: Contributor
timestamp: January 05, 2017, 08:35:52 pm
content: You can download SystemWorkbench, it's a very nice tool to develop for this platform. Once you do that just port the GPIO examples from the cube. You will be fine.

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 06, 2017, 05:09:31 am
content: Quote from: gperoni on January 05, 2017, 08:35:52 pmI'll give it a try.

author: westfw
author_rank: Super Contributor
timestamp: January 06, 2017, 11:17:15 am
content: QuotePerhaps you should show us an example of the code you are trying to use, and the actual error messages that are generated. You might be trying to use a syntax that someone will recognize as being "obviously wrong."

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 07, 2017, 04:21:25 am
content: Quote from: gperoni on January 05, 2017, 08:35:52 pmI installed it, but there is no STM32F767 when choosing the used part.I downloaded the STM32CubeF7 package, but that is just a bunch of files, among which there seems to be a ton of examples, including GPIO, but I have no idea how to load them.

author: westfw
author_rank: Super Contributor
timestamp: January 07, 2017, 09:10:24 am
content: PS. I tried my first mbed program! I didn't have any troubles modifying the basic BLINK example to do raw GPIO, as follows:Code:int main() {while(1) {GPIOA->ODR = 0; // added lineCompiled for the Nucleo 767 board. No other #includes or anything were needed.

author: gperoni
author_rank: Contributor
timestamp: January 07, 2017, 09:24:33 am
content: Have you tried updating the eclipse plugins? What's the difference between STM32F767 and for example 46? I think you just might need to edit some definitions like ram, memory space (it's not as hard as it seems and some of the other F7 options should work).

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 07, 2017, 05:35:09 pm
content: Quote from: westfw on January 07, 2017, 09:10:24 amYes, that works, in fact I have this code:Code:int main() {unsigned int i=0;GPIOD->ODR = 0xFFFFFFFF; //Opposite of pic???//RCC->AHBENR |= RCC_AHBENR_GPIOCEN; - this does not compilewhile (1){GPIOD->BSRR = i++;}}It compiles, it loads, but nothing happens.I think I am missing the clock configurations, which should be set by the RCC which is not recognized.QuoteI don't know. I am still fairly new to arm and definitely new to STM, so I still don't know how everything works here I only have experience with pic, 8051 and some atmega when it comes to microcontrollers.

author: westfw
author_rank: Super Contributor
timestamp: January 07, 2017, 10:50:33 pm
content: QuoteAh hah! Now we are getting somewhere. The F767, being a BIG FINE CHIP, has multiple buses (? Multiple AHB control registers, anyway), so anything on ONE of the AHBs is going to have a number associated with it.For GPIOC, I believe you want:Code:RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;That nicely explains why the existing examples weren't helpful - they all run on chips with simpler bus structures.I didn't really find a "friendly" table of which peripherhals are in which AHB registers, see Table 23 (page 214) in the RM0410 Reference manual. Most of the normal peripherals in in AHB1*; the STM32f767xx.h file is readable, once you figure out that you want to search for "ENR_xxxx" (but of course it's a bit difficult to GET, being distributed mainly as a part of STMCUBE...):Code:/******************** Bit definition for RCC_AHB1ENR register ***************/#define RCC_AHB1ENR_GPIOAEN 0x00000001U#define RCC_AHB1ENR_GPIOBEN 0x00000002U#define RCC_AHB1ENR_GPIOCEN 0x00000004U#define RCC_AHB1ENR_GPIODEN 0x00000008U#define RCC_AHB1ENR_GPIOEEN 0x00000010U#define RCC_AHB1ENR_GPIOFEN 0x00000020U#define RCC_AHB1ENR_GPIOGEN 0x00000040U#define RCC_AHB1ENR_GPIOHEN 0x00000080U#define RCC_AHB1ENR_GPIOIEN 0x00000100U#define RCC_AHB1ENR_GPIOJEN 0x00000200U#define RCC_AHB1ENR_GPIOKEN 0x00000400U#define RCC_AHB1ENR_CRCEN 0x00001000U#define RCC_AHB1ENR_BKPSRAMEN 0x00040000U#define RCC_AHB1ENR_DTCMRAMEN 0x00100000U#define RCC_AHB1ENR_DMA1EN 0x00200000U#define RCC_AHB1ENR_DMA2EN 0x00400000U#define RCC_AHB1ENR_DMA2DEN 0x00800000U#define RCC_AHB1ENR_ETHMACEN 0x02000000U#define RCC_AHB1ENR_ETHMACTXEN 0x04000000U#define RCC_AHB1ENR_ETHMACRXEN 0x08000000U#define RCC_AHB1ENR_ETHMACPTPEN 0x10000000U#define RCC_AHB1ENR_OTGHSEN 0x20000000U#define RCC_AHB1ENR_OTGHSULPIEN 0x40000000U/******************** Bit definition for RCC_AHB2ENR register ***************/#define RCC_AHB2ENR_DCMIEN 0x00000001U#define RCC_AHB2ENR_JPEGEN 0x00000002U#define RCC_AHB2ENR_RNGEN 0x00000040U#define RCC_AHB2ENR_OTGFSEN 0x00000080U/******************** Bit definition for RCC_AHB3ENR register ***************/#define RCC_AHB3ENR_FMCEN 0x00000001U#define RCC_AHB3ENR_QSPIEN 0x00000002U

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 08, 2017, 12:16:31 am
content: Quote from: westfw on January 07, 2017, 10:50:33 pmYes, that works now! Thanks!The code I posted previously is not really correct, BSRR register is not what I was supposed to be using, and port direction is set with MODE register.Code:Code:int main() {unsigned int i=0,j=0;RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;GPIOD-> MODER |= (GPIO_MODER_MODER0_0|GPIO_MODER_MODER1_0) ;GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1) ;// Ensure push pull mode selected--defaultGPIOC->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR0|GPIO_OSPEEDER_OSPEEDR1);//Ensure maximum speed setting (even though it is unnecessary)//GPIOD->ODR = 0xFFFFFFFF;while (1){GPIOD->ODR = i++;for(j=0;j<3;j++);}}There is a speed limit on the GPIO, toggling it at full speeds latches the pin for some time giving out some low frequency with lots of jitter.When I slowed down the loop it started working properly again, the code above is the fastest it can toggle, and my analog oscilloscope shows it to be about 50MHz.

author: FrankBuss
author_rank: Unknown
timestamp: January 08, 2017, 12:53:25 am
content: With this code:Code:while (1) {GPIOD->ODR ^= GPIO_PIN_12;}I get 12 MHz, with my STMF4 discovery board, running at 168 MHz. Taking a look at the assembly code for it (Keil uVision), it looks like this:Code:|L8.246|LDR r1,[r0,#0]EOR r1,r1,#0x1000STR r1,[r0,#0]B |L8.246|So it should run much faster, but the APB1 peripherals can only be clocked with 42 MHz max, so I guess it adds some wait states when accessing it, and might need even longer because of reading and writing to it.Another trick for fast accessing individual GPIO pins, without loading first the value of the other 31 pins for the usual load/modify/write cycle, is using the bit-banding feature:http://mightydevices.com/?p=144Note, this works even for the internal SRAM, which is pretty cool. I couldn't find the bitband macro for CubeMX, but it is easy to calculate the address yourself. This code here uses bitbanding for the pin toggling:Code:volatile void* address = &GPIOD->ODR;uint32_t bit = 12;uint32_t bb_base = 0x42000000;uint32_t bb_ref = 0x40000000;volatile uint32_t* pin = (volatile uint32_t*)(bb_base + ((uint32_t)address - bb_ref) * 32 + bit * 4);while (1) {*pin = 1;*pin = 0;}It toggles the pin with 21.2 MHz, which is the fastest you can do for GPIO (the GPIO clock is 42 MHz). The assembly code looks like this (only showing the loop) :|L8.244|STR r2,[r0,#0]STR r1,[r0,#0]B |L8.244|Of course, you could do this with the BSRR register as well, but bitbanding is very useful in general. You shouldn't write an integer counter to ODR without loading the old value first, and masking the bits you want to change, unless you are sure that it doesn't harm any of the other hardware that are connected to your pins.BTW: If you need to output lots of data fast, and without gaps in the stream, and still want to do something in parallel with your CPU, you could try to use the SRAM controller with DMA transfer (if your STM32 chip has an SRAM model). Worked pretty well when I tried it, using two DIY 4 bit R2R DACs, despite the fact that the datasheet says that the circular mode (needed for gap-less transfers, to prepare the second buffer while the first buffer is transferred and vice-versa) is not allowed for memory to memory transfers:https://plus.google.com/+FrankBussProgrammer/posts/jQidPgbtKoW

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 08, 2017, 04:15:48 am
content: What I actually need to do is transfer data to a bunch of pic18f which are connected to the stm with the parallel slave port. Having an external bus access would be even better, and I though about it, but did not bother so far looking into it.

author: westfw
author_rank: Super Contributor
timestamp: January 08, 2017, 04:39:21 am
content: QuoteThat seems unlikely, given that the datasheet says "[size=0pt]Fast toggle capable of changing every two clock cycle", and it would result in a lot of complaints... Are you sure it's not your scope that's giving out?The way I read the datasheets and .h files, the GPIO registers are directly on the AHB bus, and don't have to go through an additional APB bridge.Code:GPIOC->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR0|GPIO_OSPEEDER_OSPEEDR1);OSPEEDR has fields for each bit. I don't know what will happen if you output to the whole ODR when different bits are set to different speeds.Use 0xFFFFFFFF for full speed on all bits

author: FrankBuss
author_rank: Unknown
timestamp: January 08, 2017, 02:14:31 pm
content: Quote from: westfw on January 08, 2017, 04:39:21 amRight, it is on the AHB bus, so why does my code result in 21 MHz? This was my code:Code:|L8.244|STR r2,[r0,#0]STR r1,[r0,#0]B |L8.244|I even unrolled the loop 8 times (16 STR instructions), but no changes. Looks like the STR instruction needs always 1 cycle on a Cortex M4:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/CHDIJAFG.htmlI have the instruction cache enabled, to avoid the flash wait states. With 168 MHz CPU clock and the same AHB clock (I verified it in the CubeMX clock configuration, HCLK to AHB clock output is 168 MHz) the unrolled version should create bursts of 84 MHz, interrupted by the time for the branch instruction, or at least 42 MHz, if it is only possible to toggle it every two clocks.QuoteThe speed is only for the slew rate, it doesn't change the rate how fast you can toggle the pins.

author: FrankBuss
author_rank: Unknown
timestamp: January 08, 2017, 02:19:22 pm
content: Quote from: Dajgoro on January 08, 2017, 04:15:48 amThe application noteAN4666looks interesting for this. Nice trick to configure the timer for PWM and use it to trigger the DMA to transfer the data, and using the PWM output in addition for a clock line.

author: Dajgoro
author_rank: Frequent Contributor
timestamp: January 08, 2017, 08:29:00 pm
content: Quote from: westfw on January 08, 2017, 04:39:21 amMy scope is not from this era, but it still manages to pick up signals.I took some photos showing what is going on:A - Shows the fastest code I have runningB - Even that code generates some sort of glitch, its rare so it was a bit hard to catch on the oscilloscopeC - Shows the signal with no delay loop, and as you may see on the slew rate, its pretty slow. When I zoom in there is no other signal except noise.