SparkFun Forums 

Where electronics enthusiasts find answers.

Everything with the AmbiqSuite SDK tools and software are welcome here.
User avatar
By SidPrice
#218058
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.

Sid
User avatar
By liquid.soulder
#218184
Hi Sid

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.
https://github.com/sparkfun/AmbiqSuiteSDK

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.
https://github.com/sparkfun/SparkFun_Ap ... Suite_BSPs

Within the BSPs repo there is an I2C example. It is super basic but it could be a good starting point
https://github.com/sparkfun/SparkFun_Ap ... amples/i2c

Hope that helps, shout if there are any questions. If you find bugs please submit an issue on GitHub.

Best of luck!
User avatar
By SidPrice
#218190
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.

Again, thanks,
Sid
User avatar
By SidPrice
#218191
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
User avatar
By Menaures
#219122
Hi Sid,

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.
I tried to follow along the datasheet for the ST LSM6DSOX, which can be found here for reference: https://www.st.com/en/mems-and-sensors/lsm6dsox.html.
If you could provide me with a simply read and write example, that would be really highly appreciated.

Menaures
User avatar
By SidPrice
#219151
Menaures,

This is the read method of my application:
Code: Select all
//*****************************************************************************
//
// Read "size" data into "pBuf" to the usfsmax
//
//  The "reg" parameter is the address of the register to be read.
//
//*****************************************************************************
void usfsmax_read(uint32_t reg, uint32_t *pBuf, uint32_t size)
{
    am_hal_iom_transfer_t Transaction;

    Transaction.ui32InstrLen = 1; // registers are single byte
    Transaction.ui32Instr = reg;
    Transaction.eDirection = AM_HAL_IOM_RX;
    Transaction.ui32NumBytes = size;
    Transaction.pui32RxBuffer = pBuf;
    Transaction.bContinue = false;
    Transaction.ui8RepeatCount = 0;
    Transaction.ui32PauseCondition = 0;
    Transaction.ui32StatusSetClr = 0;
    Transaction.uPeerInfo.ui32I2CDevAddr = USFSMAX_I2C_ADDR;
    am_hal_iom_blocking_transfer(g_I2C1Handle, &Transaction);
}
I hope this helps,
Sid
User avatar
By Menaures
#219165
Hi Sid,

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?

Thanks,
Menaures
User avatar
By SidPrice
#219174
First you need to create a configuration for the I2C:
Code: Select all
static am_hal_iom_config_t g_sIOMI2cConfig =
    {
        .eInterfaceMode = AM_HAL_IOM_I2C_MODE,
        .ui32ClockFreq = AM_HAL_IOM_100KHZ,
};
Then you use this to configure the IOM:
Code: Select all
    am_hal_iom_configure(g_I2C1Handle, &g_sIOMI2cConfig);

    am_bsp_iom_pins_enable(iomModule, AM_HAL_IOM_I2C_MODE);
Is the device address USFSMAX_I2C_ADDR ...
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.

Good luck
User avatar
By Menaures
#219177
Thanks again,
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...

with kind regards,
Menaures
User avatar
By Menaures
#219199
SidPrice wrote: Wed Aug 19, 2020 10:43 am 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.
User avatar
By SidPrice
#219202
Here is my write code:
Code: Select all
//*****************************************************************************
//
//  Write "size" data from "pBuf"
//
//  Note:
//      The address to be written to must be included in the passed buffer and
//      the size parameter.
//
//*****************************************************************************
void usfsmax_write(uint32_t *pBuf, uint32_t size)
{
    am_hal_iom_transfer_t Transaction;

    Transaction.ui32InstrLen = 0;
    Transaction.ui32Instr = 0;
    Transaction.eDirection = AM_HAL_IOM_TX;
    Transaction.ui32NumBytes = size;
    Transaction.pui32TxBuffer = pBuf;
    Transaction.bContinue = false;
    Transaction.ui8RepeatCount = 0;
    Transaction.ui32PauseCondition = 0;
    Transaction.ui32StatusSetClr = 0;
    Transaction.uPeerInfo.ui32I2CDevAddr = USFSMAX_I2C_ADDR;
    am_hal_iom_blocking_transfer(g_I2C1Handle, &Transaction);
}
User avatar
By Oxcart
#219209
There are working examples of both an I2C (servo driver) and SPI (imu) device here:

https://github.com/0xcart/artemis

Refer to the README to understand how to use/setup IOM.
See artemis_i2c.c and artemis_spi.c for respective send/receive/transfer functions.
See artemis_pca9685_initialize() in artemis_pca9685.c for how to setup the I2C device.
See artemis_icm20649_initialize() in artemis_icm20649.c for how to setup the SPI device.
User avatar
By Oxcart
#219538
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.
User avatar
By james@techne
#221179
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:
Code: Select all
#define DEVICE_ADDR (0xEE)
#define DEVICE_ADDR_R (DEVICE_ADDR | 0x01)
#define DEVICE_ADDR_W (DEVICE_ADDR & 0xFE)
In the main function when configuring the transfer, they use the DEVICE_ADDR_W definition and the AM_HAL_IOM_TX definition.
Code: Select all
xfer.uPeerInfo.ui32I2CDevAddr = DEVICE_ADDR_W;
xfer.eDirection = AM_HAL_IOM_TX;
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:
1) 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.
2) 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.
 Topic permissions

You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum