I'm working a project that I'm porting from my Arduino, the main reason being that I need the extra speed. My question is about switching an entire port at once using one instruction. On the Arduino, this was accomplished by setting DDRx to hex values such as 0xFF or 0xA4. Is there a similar command for Maple? I am going to use pins PB8-PB15, as my external device is 5V has an 8bit bus that I am interfacing directly.
Atomic Port Switching
(13 posts) (5 voices)-
Posted 5 years ago #
-
There are similar commands, the reference manual displays them on page 146.
Posted 5 years ago # -
Hi,
If you mean that you want to set all eight bits at the same time you can use the BSRR register (bit set/reset) of the port. It's a 32 bit register where the upper 16 is for reset (of PORTx0-15); if you write a 1 in a position the corresponding bit will be set to zero, if you write a 0 nothing will happen (it's also a BRR (bit reset) register where you can do the same thing in the lower 16 bits). The lower 16 bits in BSRR is used to set bits, if you write a 1 the bit will be set to 1, a 0 and nothing happens.
Maybe this is totally confusing. But. If you have a byte variable, f.ex "uint8 i = 123", and you want to put it out on the high byte of PORTB in one go, you can do
(GPIOB_BASE)->BSRR = 0xFF00<<16 | i<<8;
(or with stm32-lib "GPIOB->BSRR = 0xFF00<<16 | i<<8;")
where 0xFF00<<16 will clear the bits and i<<8 will set PB8-15 to i (set have higher priority than clear).But. I'm just learning the basics of stm32, so there may be a better (or more correct) way to do this.
Posted 5 years ago # -
i think you should be able to write straight to GPIOB_BASE->ODR with a 16 bit value and have it be reflected all at once
Posted 5 years ago # -
perry
i think you should be able to write straight to GPIOB_BASE->ODR with a 16 bit value and have it be reflected all at once
Agreed, but there is a caveat.
I believe that would change the other 8 bits on the port (i.e. all 16 bits of of PB0-15), which may not be what the OP wants.Posted 5 years ago # -
In this case it's probably ok to do a read-modify-write on the ODR, like
(GPIOB_BASE)->ODR &= ~0xFF00; (GPIOB_BASE)->ODR |= i<<8;
but then it won't be an atomic access to the port.
From the reference manual:Note: For atomic bit set/reset, the ODR bits can be individually set and cleared by writing to the GPIOx_BSRR register (x = A .. E).
By the way, I think it would be nice to have parenthesis around the definitions of GPIOx_BASE in gpio.h so you can write GPIOB_BASE->ODR instead of (GPIOB_BASE)->ODR.
Posted 5 years ago # -
I'm just worried about an interrupt cutting me off halfway while I perform this operation, as I'm using the Maple to talk to a very old processor, so if anything is off, it might throw the whole circuit off. It is true there are other inputs I am using on the B register, but I can relocate them if this prove to be a bigger problem than I thought. But I will try all your suggestions, thank you.
Posted 5 years ago # -
Hi again!
I probably made a bigger issue of this than it probably is. But I can try to explain.
So you want to put out 8 bits on GPIOB[15:8]. Say you have the data in a 8 bit variable i.
This can be done in a couple of ways.
One way is to write eight digitalWrite's where each one masks out a bit in i and put it out on the corresponding pin, or the same thing in a for loop. It's probably ok to do it this way, maybe a little unefficient, but the bits will eventually get there, even if there are one or more interrupts in the mean time. And no other bits of GPIOB will be affected. Maybe your other device that's is listening on the port will be confused by the bits coming one at a time, I don't know.
Another way is to take it to a lower level and use the GPIO-ports direct. For example:
(GPIOB_BASE)->ODR = i << 8;
The port-write will be atomic (when the value of i is written to the port it can't be interrupted). But it will also set GPIOB[7:0] to zero, which may not be what you want. Even if it doesn't matter in your application I think It's a bad way to do it (you can later in the project start to use the lower bits in port B as outputs and will get some hard to find bugs).A third (or fourth) way to do it is to first read the port and then mask in the bits in i and then write it back. Just like my last example. And that sequence is what's going to happen for each of the two lines I wrote. It's probably going to work. But it's not atomic operations on the port.
If you have an interrupt routine that will modify the other (lower) bits of GPIOB.ODR, there's a chance that this interrupt will fire between the read and the write. In that case, and only that, you will undo that modification when the bits are written back. In some circumstanses (spelling?) this doesn't matter and in some it do. Only You know.
If this is a problem (probably not) you can do as I first suggested. It will modify the upper half in one single instruction without modifying the other bits.
I hope I didn't do it even more complicated. (Well it is, before you get the bits in place :-))
Posted 5 years ago # -
In my particular application, I can avoid using the other ports in the B register, but it's inevitable if you need to squeeze every cycle out and don't want to mess with assembly. I'll try the (GPIOB_BASE)->ODR = i << 8; suggestion, thanks.
Another question, how do you read a port in such a way that it return a hexadecimal value?
e.g. I have 3 pin connected on PA8, PA9, and PA10, and I would like to read the entire A register and return a value based on which of those pins are high, if PA8 and PA10 are high it would return 0B0000010100000000 or 0x500; or perhaps a way to read only the upper 8 bits which would return 0B00000101 or 0x05Would it be (GPIOA_BASE)->IDR << 8;?
Where can we find functions such as ODR, IDR GIOB_BASE and such documented?
Posted 5 years ago # -
For some reason, Pin 29, PB10, doesn't respond to (GPIOB_BASE)->ODR = 0xFF << 8; . Why doesn't this work? PB8,PB9, and PB11-15 function just as expected.
Posted 5 years ago # -
How fast do you need to go?
snigelen's more precise:
(GPIOB_BASE)->BSRR = 0xFF00<<16 | i<<8;
only updates the appropriate pins, and is only likely a few cycles (at 72MHz, say 14, 28 or 42 nSec) slower.Another question, how do you read a port in such a way that it return a hexadecimal value?
e.g. I have 3 pin connected on PA8, PA9, and PA10, and I would like to read the entire A register and return a value based on which of those pins are high, if PA8 and PA10 are high it would return 0B000001010000000int val = ((GPIOA_BASE)->IDR) >> 8; //shift bit 8 to become bit 0 val &= 0x07; // trim off other bits
more directly:
int val = (((GPIOA_BASE)->IDR) >> 8) & 0x07;
The value is always binary, so the thing that makes it look like hex is to output it using a version of print(val, HEX).
Where can we find functions such as ODR, IDR GIOB_BASE and such documented?
They are documented in an ST document called RM0008 "STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM-based 32-bit MCUs" section 8
"General-purpose and alternate-function I/Os (GPIOs and AFIOs)", starting at page 146.You'll find the document at http://www.st.com/mcu/familiesdocs-110.html
The current link to RM0008 is http://www.st.com/stonline/products/literature/rm/13902.pdf This might change when a new version is released.The STM32F is *much* more flexible than the Atmel ATmega's; I found it quite hard work to read.
Posted 5 years ago # -
For some reason, Pin 29, PB10, doesn't respond to (GPIOB_BASE)->ODR = 0xFF << 8; . Why doesn't this work? PB8,PB9, and PB11-15 function just as expected.
Looking at Maples schematics, I can't see any reason for PB10 to behave any differently from its neighbours - it should "Just Work" (TM).
What else have you got connected?
What's your setup() look like?Posted 5 years ago # -
This sketch works for me with a LED and a resistor connected to each pin:
void setup() { // PB8-15 output pinMode(14, OUTPUT); pinMode(24, OUTPUT); pinMode(29, OUTPUT); pinMode(30, OUTPUT); pinMode(31, OUTPUT); pinMode(32, OUTPUT); pinMode(33, OUTPUT); pinMode(34, OUTPUT); } uint8 i = 0; void loop() { // Binary count on GPIOB[15:8] (GPIOB_BASE)->BSRR = 0xFF00<<16 | i++<<8; delay(100); }
Posted 5 years ago #
Reply
You must log in to post.