Using SPI to communicate with L6470 and nRF52832

Hello,

I previously posted that I had issues working with communicating with the L6470 stepper driver from ST Micro using the nRF52832.

https://devzone.nordicsemi.com/f/nordic-q-a/104750/struggling-with-spi-on-nrf52832-communicating-with-l6470

I have since done something with that project to break it and decided to create a new project. 

Attached is my new code.  I tried to build this with SDK 2.5.0.

spi_L6470_ncs_2_5_0.zip

What is strange is that it looks like it builds successfully as it shows the memory usage and then shows an error after that.

Building spi_l6470
C:\WINDOWS\system32\cmd.exe /d /s /c "west build --build-dir c:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build c:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470"

[1/1] Linking C executable zephyr\zephyr.elf
FAILED: zephyr/zephyr.elf zephyr/zephyr.map zephyr/zephyr.hex zephyr/zephyr.bin zephyr/zephyr.stat
cmd.exe /C "cd . && C:\ncs\toolchains\c57af46cb7\opt\zephyr-sdk\arm-zephyr-eabi\bin\arm-zephyr-eabi-gcc.exe  -gdwarf-4 zephyr/CMakeFiles/zephyr_final.dir/misc/empty_file.c.obj zephyr/CMakeFiles/zephyr_final.dir/isr_tables.c.obj -o zephyr\zephyr.elf  -fuse-ld=bfd  -T  zephyr/linker.cmd  -Wl,-Map=C:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build/zephyr/zephyr_final.map  -Wl,--whole-archive  app/libapp.a  zephyr/libzephyr.a  zephyr/arch/common/libarch__common.a  zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a  zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a  zephyr/arch/arch/arm/core/aarch32/mpu/libarch__arm__core__aarch32__mpu.a  zephyr/lib/libc/picolibc/liblib__libc__picolibc.a  zephyr/lib/libc/common/liblib__libc__common.a  zephyr/soc/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a  zephyr/soc/soc/arm/nordic_nrf/nrf52/libsoc__arm__nordic_nrf__nrf52.a  zephyr/drivers/clock_control/libdrivers__clock_control.a  zephyr/drivers/console/libdrivers__console.a  zephyr/drivers/gpio/libdrivers__gpio.a  zephyr/drivers/pinctrl/libdrivers__pinctrl.a  zephyr/drivers/serial/libdrivers__serial.a  zephyr/drivers/spi/libdrivers__spi.a  zephyr/drivers/timer/libdrivers__timer.a  modules/nrf/lib/fatal_error/lib..__nrf__lib__fatal_error.a  modules/hal_nordic/nrfx/libmodules__hal_nordic__nrfx.a  modules/segger/libmodules__segger.a  -Wl,--no-whole-archive  zephyr/kernel/libkernel.a  zephyr/CMakeFiles/offsets.dir/./arch/arm/core/offsets/offsets.c.obj  -L"c:/ncs/toolchains/c57af46cb7/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/thumb/v7e-m/nofp"  -LC:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build/zephyr  -lgcc  -Wl,--print-memory-usage  zephyr/arch/common/libisr_tables.a  -mcpu=cortex-m4  -mthumb  -mabi=aapcs  -mfp16-format=ieee  -Wl,--gc-sections  -Wl,--build-id=none  -Wl,--sort-common=descending  -Wl,--sort-section=alignment  -Wl,-u,_OffsetAbsSyms  -Wl,-u,_ConfigAbsSyms  -nostdlib  -static  -Wl,-X  -Wl,-N  -Wl,--orphan-handling=warn  -Wl,-no-pie  -DPICOLIBC_INTEGER_PRINTF_SCANF  --specs=picolibc.specs  -lc  -lgcc && cmd.exe /C "cd /D C:\code\009-Nordic-projects\nrfconnect\spi_L6470_ncs_2_5_0\spi_l6470\build\zephyr && C:\ncs\toolchains\c57af46cb7\opt\bin\cmake.exe -E copy zephyr_final.map zephyr.map && C:\ncs\toolchains\c57af46cb7\opt\zephyr-sdk\arm-zephyr-eabi\bin\arm-zephyr-eabi-objcopy.exe --gap-fill 0xff --output-target=ihex --remove-section=.comment --remove-section=COMMON --remove-section=.eh_frame zephyr.elf zephyr.hex && C:\ncs\toolchains\c57af46cb7\opt\zephyr-sdk\arm-zephyr-eabi\bin\arm-zephyr-eabi-objcopy.exe --gap-fill 0xff --output-target=binary --remove-section=.comment --remove-section=COMMON --remove-section=.eh_frame zephyr.elf zephyr.bin && C:\ncs\toolchains\c57af46cb7\opt\zephyr-sdk\arm-zephyr-eabi\bin\arm-zephyr-eabi-readelf.exe -e zephyr.elf > zephyr.stat && C:\ncs\toolchains\c57af46cb7\opt\bin\python.exe C:/ncs/v2.5.0/zephyr/scripts/build/check_init_priorities.py --build-dir C:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build/zephyr/.. --edt-pickle C:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build/zephyr/edt.pickle""
Memory region         Used Size  Region Size  %age Used
           FLASH:       28976 B       512 KB      5.53%
             RAM:        5900 B        64 KB      9.00%
        IDT_LIST:          0 GB         2 KB      0.00%
ERROR: /soc/spi@40004000/l6470@0 POST_KERNEL 0 108 < /soc/spi@40004000 POST_KERNEL 70 107
ninja: build stopped: subcommand failed.
FATAL ERROR: command exited with status 1: 'C:\ncs\toolchains\c57af46cb7\opt\bin\cmake.EXE' --build 'c:\code\009-Nordic-projects\nrfconnect\spi_L6470_ncs_2_5_0\spi_l6470\build'

 *  The terminal process terminated with exit code: 1.
 *  Terminal will be reused by tasks, press any key to close it.

If anyone can assist with debugging that would be much appreciated.

Thanks.

I am trying to use SPI1 to communicate with the L6470. 

  • Hello,

    I will look into this after the weekend. In the meantime, you could perhaps check if changing from 'spi1' to for example 'spi2' makes any difference.

  • Hello,

    Thanks for the suggestion.  I tried switching it to the SPI2 but I don't think that worked either.

    Now it reports the following

    -edt-pickle C:/code/009-Nordic-projects/nrfconnect/spi_L6470_ncs_2_5_0/spi_l6470/build/zephyr/edt.pickle""
    Memory region         Used Size  Region Size  %age Used
               FLASH:       28972 B       512 KB      5.53%
                 RAM:        5900 B        64 KB      9.00%
            IDT_LIST:          0 GB         2 KB      0.00%
    ERROR: /soc/spi@40023000/l6470@0 POST_KERNEL 0 108 < /soc/spi@40023000 POST_KERNEL 70 107
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: 'C:\ncs\toolchains\c57af46cb7\opt\bin\cmake.EXE' --build 'c:\code\009-Nordic-projects\nrfconnect\spi_L6470_ncs_2_5_0\spi_l6470\build

  • So i am not exactly sure what is the problem but in l6470.c file for the init it had the following

    #define L64X0_INIT(n)							\
    	static const struct l64x0_config l64x0_cfg_##n = {		\
    		.spi = SPI_DT_SPEC_INST_GET(n,				\
    					    SPI_OP_MODE_MASTER |	\
    					    SPI_MODE_CPOL | 		\
    					    SPI_MODE_CPHA |		\
    					    SPI_TRANSFER_MSB |		\
    					    SPI_WORD_SET(8) |		\
    					    SPI_LOCK_ON,		\
    					    0),				\
    	};								\
    									\
    	DEVICE_DT_INST_DEFINE(n,					\
    			      &l64x0_init,				\
    			      NULL,					\
    			      NULL,					\
    			      &l64x0_cfg_##n,				\
    			      POST_KERNEL,				\
    			      0,					\
    			      NULL);
    
    DT_INST_FOREACH_STATUS_OKAY(L64X0_INIT)

    I changed it to the following:

    #define L64X0_INIT(n)							\
    	static const struct l64x0_config l64x0_cfg_##n = {		\
    		.spi = SPI_DT_SPEC_INST_GET(n,				\
    					    SPI_OP_MODE_MASTER |	\
    					    SPI_MODE_CPOL | 		\
    					    SPI_MODE_CPHA |		\
    					    SPI_TRANSFER_MSB |		\
    					    SPI_WORD_SET(8) |		\
    					    SPI_LOCK_ON,		\
    					    0),				\
    	};								\
    									\
    	DEVICE_DT_INST_DEFINE(n,					\
    			      &l64x0_init,				\
    			      NULL,					\
    			      NULL,					\
    			      &l64x0_cfg_##n,				\
    			      POST_KERNEL,				\
    			      90,					\
    			      NULL);
    
    DT_INST_FOREACH_STATUS_OKAY(L64X0_INIT)

    Changing the the parameter after POST_KERNEL from a 0 to 90.  I don't know what that values represents.  The code then builds and will run on the nRF52832.

    /*
     * Copyright (c) 2023 Space Cubics, LLC.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #define DT_DRV_COMPAT st_l6470
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(l64x0, LOG_LEVEL_DBG);
    
    #include "l6470.h"
    
    #include <zephyr/device.h>
    #include <zephyr/drivers/spi.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/sys/util.h>
    #include <stdlib.h>
    
    struct l64x0_config {
    	struct spi_dt_spec spi;
    };
    
    /* Commands */
    #define RX_BYTES_NONE (0)
    #define RX_BYTES_GET_STATUS (2)
    
    #define TX_BYTES_NONE (0)
    #define TX_BYTES_RUN (3)
    #define TX_BYTES_MOVE (3)
    
    #define CMD_NOP          ((0))
    
    #define CMD_SET_PARAM    ((0 << 5))
    #define CMD_GET_PARAM    ((1 << 5))
    
    #define CMD_MOVE         ((2 << 5))
    #define CMD_RUN          ((2 << 5) | BIT(4))
    #define CMD_STEP_CLOCK   ((2 << 5) | BIT(4) | BIT(3))
    
    #define CMD_GOTO         ((3 << 5))
    #define CMD_GOTO_DIR     ((3 << 5) |          BIT(3))
    #define CMD_GO_HOME      ((3 << 5) | BIT(4))
    #define CMD_GO_MARK      ((3 << 5) | BIT(4) | BIT(3))
    
    #define CMD_GO_UNTIL     ((4 << 5) |                   BIT(1))
    #define CMD_RELEASE_SW   ((4 << 5) | BIT(4)            BIT(1))
    
    #define CMD_SOFT_HIZ     ((5 << 5))
    #define CMD_HARD_HIZ     ((5 << 5) |          BIT(3))
    #define CMD_SOFT_STOP    ((5 << 5) | BIT(4))
    #define CMD_HARD_STOP    ((5 << 5) | BIT(4) | BIT(3))
    
    #define CMD_RESET_DEVICE ((6 << 5))
    #define CMD_GET_STATUS   ((6 << 5) | BIT(4))
    #define CMD_RESET_POS    ((6 << 5) | BIT(4) | BIT(3))
    
    //#define CONFIG_L6470 (1)
    
    #if IS_ENABLED(CONFIG_L6470)
    static int bit_len[] = {
            [L64x0_ADDR_ABS_POS]    = 22,
            [L64x0_ADDR_EL_POS]     = 9,
            [L64x0_ADDR_MARK]       = 22,
            [L64x0_ADDR_SPEED]      = 20,
            [L64x0_ADDR_ACC]        = 12,
            [L64x0_ADDR_DEC]        = 12,
            [L64x0_ADDR_MAX_SPEED]  = 10,
            [L64x0_ADDR_MIN_SPEED]  = 13,
            [L64x0_ADDR_KVAL_HOLD]  = 8,
            [L64x0_ADDR_KVAL_RUN]   = 8,
            [L64x0_ADDR_KVAL_ACC]   = 8,
            [L64x0_ADDR_KVAL_DEC]   = 8,
            [L64x0_ADDR_INT_SPEED]  = 14,
            [L64x0_ADDR_ST_SLP]     = 8,
            [L64x0_ADDR_FN_SLP_ACC] = 8,
            [L64x0_ADDR_FN_SLP_DEC] = 8,
            [L64x0_ADDR_K_THERM]    = 4,
            [L64x0_ADDR_ADC_OUT]    = 5,
            [L64x0_ADDR_OCD_TH]     = 4,
            [L64x0_ADDR_STALL_TH]   = 7,
            [L64x0_ADDR_FS_SPD]     = 10,
            [L64x0_ADDR_STEP_MODE]  = 8,
            [L64x0_ADDR_ALARM_EN]   = 8,
            [L64x0_ADDR_CONFIG]     = 16,
            [L64x0_ADDR_STATUS]     = 16,
    };
    #elif IS_ENABLED(CONFIG_L6480)
    static int bit_len[] = {
            [L64x0_ADDR_ABS_POS]    = 22,
            [L64x0_ADDR_EL_POS]     = 9,
            [L64x0_ADDR_MARK]       = 22,
            [L64x0_ADDR_SPEED]      = 20,
            [L64x0_ADDR_ACC]        = 12,
            [L64x0_ADDR_DEC]        = 12,
            [L64x0_ADDR_MAX_SPEED]  = 10,
            [L64x0_ADDR_MIN_SPEED]  = 12,
            [L64x0_ADDR_KVAL_HOLD]  = 8,
            [L64x0_ADDR_KVAL_RUN]   = 8,
            [L64x0_ADDR_KVAL_ACC]   = 8,
            [L64x0_ADDR_KVAL_DEC]   = 8,
            [L64x0_ADDR_INT_SPEED]  = 14,
            [L64x0_ADDR_ST_SLP]     = 8,
            [L64x0_ADDR_FN_SLP_ACC] = 8,
            [L64x0_ADDR_FN_SLP_DEC] = 8,
            [L64x0_ADDR_K_THERM]    = 4,
            [L64x0_ADDR_ADC_OUT]    = 5,
            [L64x0_ADDR_OCD_TH]     = 5,
            [L64x0_ADDR_STALL_TH]   = 5,
            [L64x0_ADDR_FS_SPD]     = 11,
            [L64x0_ADDR_STEP_MODE]  = 4,
            [L64x0_ADDR_ALARM_EN]   = 8,
            [L64x0_ADDR_GATECFG1]   = 11,
            [L64x0_ADDR_GATECFG2]   = 8,
            [L64x0_ADDR_CONFIG]     = 16,
            [L64x0_ADDR_STATUS]     = 16,
    };
    #endif
    
    #define BITS_TO_BYTES(x) (((x) + 7) >> 3)
    
    static int send_command(const struct device *const dev, uint8_t cmd, int val, int tx_bytes, int rx_bytes)
    {
    	const struct l64x0_config *config = dev->config;
    	const struct spi_dt_spec *spec = &config->spi;
            uint8_t value;
            struct spi_buf bufs;
            struct spi_buf_set bufset;
            bufset.buffers = &bufs;
            bufset.count = 1;
            bufs.buf = &cmd;
            bufs.len = 1;
            int ret = 0;
    
            spi_write_dt(spec, &bufset);
    
            bufs.buf = &value;
    
            switch (tx_bytes) {
            case 0:
                    break;
            case 1:
                    value = val & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    break;
            case 2:
                    value = (val >> 8) & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    value = val & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    break;
            case 3:
                    value = (val >> 16) & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    value = (val >> 8) & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    value = val & 0xff;
                    spi_write_dt(spec, &bufset);
    
                    break;
            default:
                    break;
            }
    
            switch (rx_bytes) {
            case 0:
                    break;
            case 1:
                    /* FIXME: this breaks SPI communication */
                    //k_busy_wait(10);
                    spi_read_dt(spec, &bufset);
                    ret = value;
    
                    break;
            case 2:
                    spi_read_dt(spec, &bufset);
                    ret |= value << 8;
    
                    spi_read_dt(spec, &bufset);
                    ret |= value;
    
                    break;
            case 3:
                    spi_read_dt(spec, &bufset);
                    ret |= value << 16;
    
                    spi_read_dt(spec, &bufset);
                    ret |= value << 8;
    
                    spi_read_dt(spec, &bufset);
                    ret |= value;
    
                    break;
            default:
                    LOG_WRN("rx_bytes is %d for cmd %x", rx_bytes, cmd);
                    ret = -1;
    
                    break;
            }
    
            return ret;
    }
    
    static int send_command_simple(const struct device *const dev, uint8_t cmd)
    {
            return send_command(dev, cmd, 0, TX_BYTES_NONE, RX_BYTES_NONE);
    }
    
    int l64x0_nop(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_NOP);
    }
    
    void l64x0_setparam(const struct device *const dev, uint8_t param, uint32_t val)
    {
            int tx_bytes;
    
            if (param == 0 || L64x0_ADDR_LAST <= param) {
                    LOG_ERR("param = %x", param);
                    return;
            }
    
            tx_bytes = BITS_TO_BYTES(bit_len[param]);
    
            send_command(dev, param, val, tx_bytes, RX_BYTES_NONE);
    }
    
    int l64x0_getparam(const struct device *const dev, uint8_t param)
    {
            int rx_bytes;
    
            if (param == 0 || L64x0_ADDR_LAST <= param) {
                    LOG_ERR("param = %x", param);
                    return -EINVAL;
            }
    
            rx_bytes = BITS_TO_BYTES(bit_len[param]);
    
            return send_command(dev, CMD_GET_PARAM | param, 0, TX_BYTES_NONE, rx_bytes);
    }
    
    int l64x0_run(const struct device *const dev, int speed)
    {
            bool dir = speed >= 0;
    
    	return send_command(dev, CMD_RUN | dir, abs(speed), TX_BYTES_RUN, RX_BYTES_NONE);
    }
    
    int l64x0_move(const struct device *const dev, int n_step)
    {
            bool dir = n_step >= 0;
    
            send_command(dev, CMD_MOVE | dir, abs(n_step), TX_BYTES_MOVE, RX_BYTES_NONE);
    
            return 0;
    }
    
    int l64x0_reset_device(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_RESET_DEVICE);
    }
    
    int l64x0_soft_stop(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_SOFT_STOP);
    }
    
    int l64x0_hard_stop(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_HARD_STOP);
    }
    
    int l64x0_soft_hiz(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_SOFT_HIZ);
    }
    
    int l64x0_hard_hiz(const struct device *const dev)
    {
            return send_command_simple(dev, CMD_HARD_HIZ);
    }
    
    int l64x0_get_status(const struct device *const dev)
    {
            return send_command(dev, CMD_GET_STATUS, 0, TX_BYTES_NONE, RX_BYTES_GET_STATUS);
    }
    
    int l64x0_init(const struct device *dev)
    {
    	return 0;
    }
    
    #define L64X0_INIT(n)							\
    	static const struct l64x0_config l64x0_cfg_##n = {		\
    		.spi = SPI_DT_SPEC_INST_GET(n,				\
    					    SPI_OP_MODE_MASTER |	\
    					    SPI_MODE_CPOL | 		\
    					    SPI_MODE_CPHA |		\
    					    SPI_TRANSFER_MSB |		\
    					    SPI_WORD_SET(8) |		\
    					    SPI_LOCK_ON,		\
    					    0),				\
    	};								\
    									\
    	DEVICE_DT_INST_DEFINE(n,					\
    			      &l64x0_init,				\
    			      NULL,					\
    			      NULL,					\
    			      &l64x0_cfg_##n,				\
    			      POST_KERNEL,				\
    			      90,					\
    			      NULL);
    
    DT_INST_FOREACH_STATUS_OKAY(L64X0_INIT)
    

Related