While I am an experienced programmer I am struggling to wrap my head around the Ambiq SDK, it appears to lack any high-level conceptual description of how their API is structured. Yes, there are lots of example projects in the SDK, however, I have failed to find a simple I2C master example. I am using the Apollo3 EVB.
I would really appreciate pointers to an example for implementing an I2C master that operates with an external slave device. Failing that, if someone knows of a good getting started with the Ambiq SDK that presents the API structure and usage that would certainly help.
We have found the same thing. For me the best way to utilize the SDK and HAL layer is to read the header files. Of course if we all had to do that it would take a lot of duplicated effort. So I’ll point you to an example that could maybe help.
First of all - I recommend using SparkFun’s mirror of the SDK. It uses version control so it is possible to track issues and possibly even use patches for a few bugs in the SDK. It is available at the following link and the README.md file would be a good start.
The mirror of the SDK contains a reference to the SparkFun BSPs repository - which contains board definitions for SF boards and also examples. Again, the README.md file is a good place to start - it will tell you how to build examples.
Hi and many thanks for taking the time to respond.
I have made a little progress since I posted my appeal for an example. And, as you suggested, reading the code and the header files of the Apollo SDK seems to be the only way to get any progress, what a PITA.
My I2C code is reading from my device, however, my write function is not working. I notice in the example you linked to in the SparkFun mirror, the “offset” entry in the transaction structure is not used. In my reading function I placed the “command” to the I2C device in the “offset” entry and set the instruction length to “1”. That appears not to work with the TX operation.
I will give the approach in the linked example a try and see if that works.
I am working on a project including the Artemis Nano and also struggling to fully understand the am_hal_iom_nonblocking_transfer function. I simply need to read sensor values via I2C. I assume you were using this function to get the I2C communication to work. Would you be so kind to explain how you have used the offset to specify the sub-adress of the register you were trying to read?
What I have tried so far is to send two bytes with the first one containing the 7-bit slave address plus the write bit and the second byte being the sub-address of the register containing the sensor measurements. In a second function call I have set the read/write bit to read and the direction to read as well, expecting the sensor value to get stored in the RxBuffer. Unfortunately that did not work.
thanks for your help. This has already been helpful.
Although, for the puzzle to be complete, it might be useful to see the initialization of the g_I2C1Handle, because I don’t understand the role of the g_I2C1Handle.pNBTxnBuf.
Two further questions concering the device and register addresses just to make sure this isn’t the cause of the issue. Is the device address USFSMAX_I2C_ADDR the 7-bit or the 8-bit address with the read bit set?
Most registers have 8 or 16-bit addresses, but the instruction Transaction.ui32Instr is stored in a uint32_t, which makes me wonder whether the remaining 24-bit for a 8-bit address should be appended as a prefix or a suffix. I assume it is a prefix so a hex address 0x0F would expand to a binary 0000 0000 0000 0000 0000 0000 0000 1111, am I right?
It is the address of the I2C device you are trying to connect with, the read/write bit is NOT included in this address.
All the I2C peripheral registers are 32-bit, including the data fifo. You need to read the datasheet to understand how this works, it would take too long for me to cover that subject in this forum.
I got the reading function working by now and the configuration for the I2C is basically identical to yours.
I just realized it doesn’t matter how many address bits are being used due to the typecasting to a uint32_t. :roll:
Meanwhile I am pretty familiar with the datasheet, although it has rather led to confusion, because the am_hal_iom_blocking_transfer function is way more abstract than the low level description of the I2C communication in the datasheet.
There is just one more issue with my write function. Thus I would really appreciate if you could share this precious piece of code as well, which should finally allow me to make use of the full functionality of this amazing IMU.
One could consider to add an I2C example with your read and write functions to the boards_sfe repository, so that others have it easier to get started with I2C in the Ambiq Suite SDK…
SidPrice:
I updated my write function to not use the “offset” parameter, I now pass the “command” in the buffer. Thanks so much for that pointer.
Sid
That’s what I have tried as well. For me it didn’t work, but the same approach as with the read function did the job. So the write function is exactly the same as the read function with Rx replaced by Tx in the direction and the buffer.
Thought I would add this for the sake of completeness.
I merged an update into the repository which includes basic support for UART. It works (tested), however there is no working example in the repository.
I’ve been working on integrating a serial based RC receiver (Jeti) which outputs data per Jeti’s EX-Bus serial protocol (if you’re really interested you can look at my commit history for documentation). Long story short, the FIFO is flooded by the RX. I’ve tried numerous ways to mitigate this but have not found a working solution. As an alternative I implemented the basics to get up and running on an STM32F405. It was able to handle the data without issue.
One thing I’ve noticed on the Apollo is the exorbitant overhead of interrupts. You may notice that I don’t use one in the scheduler. I do have an alternative implementation of the scheduler that is interrupt driven but it results in an overall update rate of about 100Hz. Using the simple (time based) scheduler you currently find in master it can do about 200Hz. Put the Apollo in Burst Mode and it can do 300Hz+. If anyone has any recommendations on optimizing interrupt overhead (and yes, I keep my interrupt functions short and sweet, etc.) please let me know.
Also, if anyone has any insight into servicing the UART FIFO let me know. The FIFO is “only” 32 bytes deep, and while I am setting up buffers for the FIFO’s to read/write into, the receive FIFO is overrun. The Jeti receiver sends 40 bytes at 100Hz. Again I’ve tried numerous ways to read/service the RX UART port as fast as possible and have so far come up empty handed.
Check your I2C device datasheet to make sure they haven’t already pre-shifted the address value to make room for the read/write bit. For example, my I2C device had a listed address of 0x12 (0b00010010), but the example I2C transmission in the datasheet made it obvious that they meant the “read address” was 0x13, but the “write address” was 0x12.
The Apollo I2C example code uses the convention that the device address should have been listed as 0x09 (0b00001001). The Apollo will take whatever value you give it for the address and do a logic shift left to make room for the read/write bit. If you can get a logic analyzer to look at the I2C transmission, check to make sure that your address isn’t twice the value that you intended.
In the Apollo I2C example code, the use #defines to set the I2C address:
I haven’t confirmed this is the exact issue, but I believe that the Apollo will take that I2C address, logic shift left, and set or clear the read/write bit based on whether you configure the transfer as AM_HAL_IOM_TX or AM_HAL_IOM_RX all on its own - without you having to define the ADDR_W or ADDR_R definitions to set/clear the LSB.
So to get the example to work:
Make sure the address you’re using isn’t pre-shifted to account for the read/write bit. The peripheral will do that for you and you don’t need to do it twice.
The xfer.eDirection value will set or clear the read/write bit. You don’t need to use the DEVICE_ADDR_R or DEVICE_ADDR_W #defines in the example.