Hello, is it possible to communicate with a FT245BL with the maple mini via a parallel data bus? It's an 8 bit data bus. How would I write code for something like that? Would I make an interrupt for each clock pulse and have it read the data? How would I make the bus bidirectional? make an array and transmit that every few ms?
Thanks for your help.
Maple mini parallel data bus
(14 posts) (5 voices)-
Posted 3 years ago #
-
Not too hard. All the I/O pins can be switched between input and output. You could go either interrupt driven (easier) or DMA (faster).
Use Port A for your data as some of the Port B pins aren't brought out.
Depending on what you are doing you might find it easier to just use the on-board USB to move your data.
Posted 3 years ago # -
Thanks for the quick response, but I need the fdti because it needs to emulate a device. I already have the EEPROM data. I'm mainly
Looking for help on setting up the software. I can't decide how I want to write the software. Should I make a transmit and a receive array and then pulse the appropriate pin to either read or write to the bus? Is there an easier way to do it?Posted 3 years ago # -
That is pretty simple. If you use the FT245BL you are limited to a maximum of 1M transfers per second. (Which is also true of the on board USB.) The Maple Mini will do that easily in programmed I/O mode. One interrupt per block is probably all you need.
Posted 3 years ago # -
Could I piggy-back on this discussion, especially your response, Rod? I'm totally new to Arduino and Maple. I'm looking for the fastest way to get data in and out of a Maple (r5) from an ADC and to a DAC. One option is SPI (though this may not be fast enough, ideally looking for >~ 3MSPS 12-bit) and this is covered in the Forum. Reading your post that the Maple will easily do 1M [parallel] transfers per second in programmed I/O mode -- this looks hopeful, as opposed to mapping bits to pins and writing one I/O pin per cycle.
Could you point me to anything that explains this in some detail?
Thanks!
Posted 3 years ago # -
The easiest way of getting it done is to use Port A on the Maple Mini. PA0 - PA7 are available on D11 - D4 of the Maple Mini. You'll be loving 8 ADC channels, 5 timers, USART2, and SPI1 if you wish to use an 8-bit bus on the Maple Mini. Other options on the Maple Mini include PA8-PA15 on D27 to D20. You'll end up losing 5 timers and USART1. It is possible to use PB0-7 or PB8-15, however Port B is scattered all over and is harder to wire up.
The best way about going about it is to create a buffer (512 Byte, 10KB, etc.). When the buffer is full, or you want to send data simply dump the data to the D0-D7 part of PA (GPIO mask lock/update-only feature is amazing on the STM32!). Making sure to update the RD# and WR pins as needed, and TXE# and RXF# are at the appropriate levels. If you want to receive data, read into receive buffer, etc. You can take a look at how the USART implementation is like and mimic that. You're doing the same thing except it's parallel.
If you want to improve performance, and you don't want to manually transfer data to/from the buffer, you can use a DMA controller to handle that for you. In addition, setup interrupts to find out when you need to receive data.
Posted 3 years ago # -
Thanks robodude for your detailed reply. I gather from what you say that I can do an 8-bit write to all 8 outputs (or 16-bit to PA0-PA15) of Port A as a single instruction. I hadn't realized that. This will be a lot faster than writing sequentially one pin at a time!
I'd been looking for an instruction such as "writeByte()" or "outputByte()". In the Wiring instruction set I only see digitalWrite(), which writes one bit. I don't see an instruction that can write to an entire port in parallel. I guess there must be a way to do this. Would you possibly have a code example of a parallel write?
Thanks again
Rich
Posted 3 years ago # -
Rich Simmons,
You won't find this functionality built into the wirish Wiring instructions for the Maple. This is because it's functionality found within the lower-level C libmaple library.
In
libmaple
, each PORT is represented by aGPIOx
struct. Within this struct, amongst other things, you'll find a pointer calledregs
that points to the registers related to the PORT. Since you're dealing withPORTA
, you'll be usingGPIOA->regs
when working with PORTA's libmaple functionality. Theregs
pointer points to another struct with this definition:/** GPIO register map type */ typedef struct gpio_reg_map { __io uint32 CRL; /**< Port configuration register low */ __io uint32 CRH; /**< Port configuration register high */ __io uint32 IDR; /**< Port input data register */ __io uint32 ODR; /**< Port output data register */ __io uint32 BSRR; /**< Port bit set/reset register */ __io uint32 BRR; /**< Port bit reset register */ __io uint32 LCKR; /**< Port configuration lock register */ } gpio_reg_map;
CRL
andCRH
are used to configure the PORT.
IDR
is used for reading from the PORT.
ODR
is used for writing to the PORT.
BSRR
is used to set & reset bits in the PORT.
BRR
is used to reset bits in the PORT.
LCKR
is used to lock configuration so they cannot be changed.They are all 32-bit registers, and while I won't go into detail about all of them I will talk about
BSRR
andBRR
.libmaple
has a function calledgpio_write_bit
which looks like this:/** * Set or reset a GPIO pin. * * Pin must have previously been configured to output mode. * * @param dev GPIO device whose pin to set. * @param pin Pin on to set or reset * @param val If true, set the pin. If false, reset the pin. */ static inline void gpio_write_bit(gpio_dev *dev, uint8 pin, uint8 val) { if (val) { dev->regs->BSRR = BIT(pin); } else { dev->regs->BRR = BIT(pin); } }
and is used like:
gpio_write_bit(GPIOA, 5, true);
which will setPA5
to HIGH (or binary '1').If you use
gpio_write_bit
to set a bit to HIGH, it writes a1
to the corresponding bit's location inBSRR
. Likewise, if you set the bit to LOW, it writes a1
to the corresponding bit's location inBRR
. (This is because of the if-else statement in the function.)BRR
will set the bit to low when it's corresponding value is TRUE, or a binary1
. Values set to0
will be unchanged. For example:GPIOA->regs->BRR = 0b00001111;
will reset, or clear (set to LOW) PA0, PA1, PA2, and PA3. The values of PA4 - 15 will be unchanged. This is very handy when you need to clear multiple bits at a time, and leave certain values alone.
BSRR
has a similar functionality, however it has the ability to set AND clear corresponding bits. Because of this,BSRR
's full 32-bit width is used whileBRR
is only 16-bit wide (the upper 16-bit aren't used).BSRR
's bits 0 to 15 are used for setting bit X, while bits 16 to 31 are used to reset/clear, bits Y.1
s found within BSRR[0:15] will SET the corresponding bit.0
s found within BSRR[0:15] will not affect the position's current value.Likewise,
1
s found within BSRR[16:31] will CLEAR the corresponding bit, and0
s will not affect the current value. If there is a1
located anywhere in BSRR[0:15] (the SETting ability), it will be executed BEFORE any CLEARing is done.For example:
Let's say previous code made PA0-3 HIGH, and PA4-7 LOW. PA8-15 were untouched and remain LOW by default. If you were to read PORTA and look at only the first 8 bits, you would get:
0b00001111
. Guess what the following line would do after being executed:GPIOA->regs->BSRR = 0x5500AA;
Let's take a look at what
0x5500AA
is equal to in binary:0b00000000010101010000000010101010
Wooh! Let's break it down to the upper 16-bits (the section that will CLEAR), and the lower 16-bits (the section that will SET):Setting :
0b0000000010101010
Clearing:0b0000000001010101
Because the lower 16-bit are done first, anywhere in the lower 16-bit there is a
1
, the PORTA value will be set to a1
. The output becomes:0b10101111
temporarily. We now process the upper 16-bit. Anywhere there is a1
, the PORTA value becomes a0
. Our final output is:0b10101010
, which is then outputted to PORTA.Effectively, with one instruction (more or less) we can change all 16 bits of PORTA, or any bits within that we care about without modifying the others or having to read the current value initially (saves instructions = higher performance).
You're asking to yourself now, why does
gpio_write_bit
have an if-statement that decides between usingBSRR
andBRR
and only usesBSRR
's setting ability. Good question. I don't know; talk to one of the people that wrote it. My guess is, it's probably simpler to implement and it's not designed to be as fast as possible. However, if you are in need of very high performance, taking advantage ofBSRR
's full capabilities will greatly increase your performance. If you wanna take things easy, and make your algorithm look more reader-friendly, you can useBSRR
for it's setting capability, followed by aBRR
call that clears the other bits.Hopefully you found this helpful and not confusion. For further reading, take a look at Reference Manual for the STM32. In addition, gpio.c and gpio.h are great references for libmaple's GPIO features. Lastly, you can search around the forums for tips and tricks on optimizing GPIO performance, and other explanations/tutorials.
-robodude666
Posted 3 years ago # -
Thanks so much, robodude666, for a really thorough answer and explanation. A great help! We're prototyping it up now.
Meanwhile, we're also putting together some SPI ADCs and DACs. More questions to follow, on this super-helpful forum.
Rich Simmons
Posted 3 years ago # -
Another thing to keep in mind, if you need to transfer data between memory and a peripheral or vice versa as quickly as possible, the STM32's DMA controllers are worth taking a look at. There is plenty of documentation and examples on the forums you can use for reference.
Posted 3 years ago # -
robodude:
Awesome reply! This is definitely wiki material.
You're asking to yourself now, why does gpio_write_bit have an if-statement that decides between using BSRR and BRR and only uses BSRR's setting ability. Good question. I don't know; talk to one of the people that wrote it. My guess is, it's probably simpler to implement and it's not designed to be as fast as possible.
Yeah, I had the same thought. New version is here, will be part of the next release:
That should inline nicely presuming
pin
andval
are both constant.Also note that this example code you posted:
GPIOA->regs->BSRR = 0x5500AA;
will definitely work, but the extra pointer dereferencing could be a performance hit in an inner loop or otherwise performance-sensitive section. (You probably already know this, but I thought I'd include it here for the record.) If you know you're going to be using
GPIOA
, its worth it to write that asGPIOA_BASE->BSRR = 0x5500AA
.GPIOA_BASE
is just a macro for a pointer to the literal address of the GPIOA register map. For example, on Maple, it's this:#define GPIOA_BASE ((struct gpio_reg_map*)0x40010800)
This means that writing
GPIOA_BASE->BSRR
instead ofGPIOA->regs->BSRR
lets GCC say "load the (statically determined) address of GPIOA's BSRR into a register, and store0x5500AA
into the address pointed by that register". That can save time compared to the two extra pointer dereferences implied byGPIOA->regs->
.Posted 3 years ago # -
mbolivar,
Forgot to mention about the
GPIOx_BASE
shortcut for performance. I haven't touched my Maple since I fried a regulator back in September, and all of my hobby electronics stuff is back in NYC (I moved to Idaho for a gal earlier this year)... So I completely forgot about it.On the bright side of things, I finally got a job LOL!
-robodude666
Posted 3 years ago # -
robodude,
Congrats! Hope everything's working out for you in Idaho.
Posted 3 years ago # -
Now that I ate dinner...
I appreciate the performance tweak to
gpio_write_bit(...)
! But, because I am a performance freak like that, how would something like this compare:typedef enum gpio_write_val { GPIO_HIGH = 0, GPIO_LOW = 16 } gpio_write_val; static inline void gpio_write_bit(gpio_dev *dev, uint8 pin, gpio_write_val val) { dev->regs->BSRR = (1U << pin) << val; }
With usage:
gpio_write_bit(GPIOA, 5, GPIO_HIGH);
which will set PA5, and:gpio_write_bit(GPIOA, 5, GPIO_LOW);
which will clear PA5. You can alternatively useGPIO_SET
andGPIO_CLEAR
as the english for the enum's options.This would save one read/write variable, an invert operation, and a multiply operation. Hopefully GCC replaces enum datatypes and their values as just int values...
-robodude666
Posted 3 years ago #
Reply
You must log in to post.