Hi there,

> I checked the ITG3200 driver, and it does what I currently do and
> wanted to avoid with a custom transaction, namely that you have to
> arrange the data you want to send after the register address in
> memory. This either needs a copy, or you have to put the data directly
> in the scratch array, none of which I think is elegant, but I guess
> I'll just keep it this way for now.

Well, there is a little more to the scratch buffer than meets the eye ;-)

        // the read buffer is for a continous read from address 0x00 -> 0x09
        // (x: SMPLRT_DIV is not buffered!) (0x15)
        // 0: DLPF_FS           (0x16)
        // 1: INT_CFG           (0x17)
        // 2: INT_STATUS        (0x18)
        // 3: TEMP_OUT_H  -- also used for write address of register
        // 4: TEMP_OUT_L -- also used for write buffer[0]
        // 5: GYRO_XOUT_H  -- also used for write buffer[1]
        // 6: GYRO_XOUT_L -- also used for write buffer[2]
        // 7: GYRO_YOUT_H
        // 8: GYRO_YOUT_L
        // 9: GYRO_ZOUT_H
        // 10: GYRO_ZOUT_L      (0x22)
        // 11: PWR_MGM          (0x3E)
        uint8_t rawBuffer[12];

In particular, not all of it is available as a scratch buffer, only index 3-10, 
of which only 3-6 are used to actually perform a write transfer.
This is possible, because we store the actual sensor data in a separate `Data` 
object. Reasons are

1. Reading your sensor data does not cause “tearing” in your sensor’s data.
    Instead the data is updated all at once only _after a transfer succeeded_!
2. You can create a `struct` with all of your sensor Data objects in it, which 
you can then transfer as one continuos memory block.

        struct ATTRIBUTE_PACKED  {
                xpcc::itg3200::Data rotation;
                xpcc::lis3dsh::Data acceleration;
                xpcc::hmc58x3::Data compass;
        } sensors;
        foo.transfer((char*)sensors, sizeof(sensors));  

Note that sensor vendors are smart with the memory layout:
The `INT_STATUS` register is just above data registers; an opportunity we do 
not pass on in the `readRotation` function:

        if (RF_CALL(read(Register::INT_STATUS, rawBuffer+2, 9)))        // read 
9 bytes
        {       // copy 8 bytes into Data object in one go.
                std::memcpy(data.data, rawBuffer+3, 8);

So it makes sense to reuse the data part of the buffer for performing register 
writes or reads.

Convinced yet? No? Fine. Moar propaganda then:

We can save half the bus transfers on read-modify-write of control registers.
Since the content of control registers (like `DLPF_FS`) are usually not 
modified by the sensor hardware,
we do not have to perform an expensive read first, if we have stored its 
content in locally.
This is why the `updateRegister` function only writes once:

        updateRegister(uint8_t index, uint8_t setMask, uint8_t clearMask) 
                RF_BEGIN();     // read(local)-modify(local)-write(external)
                rawBuffer[index] = (rawBuffer[index] & ~clearMask) | setMask;
                RF_END_RETURN_CALL(write(Register(index), rawBuffer[index]));

So the scratch buffer is quite handy.

Also consider that your custom I2cTransaction will need to keep additional 
so you’ll very likely end up with more memory consumption than this solution.

> The stm32 I'm programming is on a custom board for a product,

We would be honored if “The Smart Pillow – revolutionary sleep monitor, white 
noise generator and alarm clock”,
or whatever you’re actually developing ships with xpcc ;-P

> that is, I don't use any pre-made boards.

Why not? Pre-made boards are tested…
Unless you have very specific reasons for a custom board, bringing up a new 
board is difficult :-(

> One symptom is that sometimes, when I power on the device, nothing
> happens, it seems like the cpu doesn't start up correctly or freezes
> very early.
> The other thing that happens is that after a while (sometimes minutes,
> sometimes as low as 5 seconds), when things are already working, the
> cpu freezes, and when I read the cpu state with ST-LINK it seemed like
> it was in the hard fault handler.
> Do you have any suggestions for any of these problems? Like basic
> wiring of the cpu (I already have BOOT0 on GND, didn't find anything
> else needed)

One problem we had with a custom board was that the PullUp resistor on the 
!RESET pin was too weak.
It said in the datasheet something of a few kOhm, but when we looked at the 
schematic of the ST F4 Discovery board, it only used 500 Ohm.
We exchanged it and never had problems with it again (that I know of).

So the only electrical advice I can give is to check out the schematics of the 
ST discovery boards, because these designs have _really_ been proven in 

If you want to be sure that it’s not something in xpcc that’s causing 
hardfaults, try only blinking an LED using `xpcc::delayMilliseconds(200)`.
xpcc itself does not start any activities in the background by itself, so you 
can then build up your functionality until it starts failing again.
That could give a rough idea of where a problem might be.

>  and tips on how to debug a hard fault (maybe an
> xpcc-specific solution).

You can enable a blinking LED whenever the hardfault is encountered.
This is unfortunately not the most documented thing in xpcc ever… :-(

Put this into your project.cfg (replace with the right LED pin obviously):

        core.cortex.0.enable_hardfault_handler_led = true
        core.cortex.0.hardfault_handler_led_port = E
        core.cortex.0.hardfault_handler_led_pin = 9

There is a working example of this though for the STM32F3 (also prints 
hardfault context via UART):

This is the hardfault handler btw:

I’ve improved stack safety and hard fault output significantly with this PR:
But it’s not ready to merge yet.

You can always load the .elf file (from the build/ folder) into the 
arm-none-eabi-gdb debugger and debug it manually.
I actually recommend that (especially with the `--tui` option).

Hope that helps,
xpcc-dev mailing list

Reply via email to