How does kernel code knows which spi bus is using? - c

I modified device tree file and enable spi using 4 GPIO pins, which support pinmux and switch from gpio to spi function.
But in Linux kernel code, how does the code know which spi bus/pins is used?
For example, I find a Linux kernel driver: max1111.c, which drives a spi ADC chip. But I checked its code, and don't find where the spi bus/pins is specified.
I paste max1111.c below.
/*
* max1111.c - +2.7V, Low-Power, Multichannel, Serial 8-bit ADCs
*
* Based on arch/arm/mach-pxa/corgi_ssp.c
*
* Copyright (C) 2004-2005 Richard Purdie
*
* Copyright (C) 2008 Marvell International Ltd.
* Eric Miao <eric.miao#marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* publishhed by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
enum chips { max1110, max1111, max1112, max1113 };
#define MAX1111_TX_BUF_SIZE 1
#define MAX1111_RX_BUF_SIZE 2
/* MAX1111 Commands */
#define MAX1111_CTRL_PD0 (1u << 0)
#define MAX1111_CTRL_PD1 (1u << 1)
#define MAX1111_CTRL_SGL (1u << 2)
#define MAX1111_CTRL_UNI (1u << 3)
#define MAX1110_CTRL_SEL_SH (4)
#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */
#define MAX1111_CTRL_STR (1u << 7)
struct max1111_data {
struct spi_device *spi;
struct device *hwmon_dev;
struct spi_message msg;
struct spi_transfer xfer[2];
uint8_t tx_buf[MAX1111_TX_BUF_SIZE];
uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
struct mutex drvdata_lock;
/* protect msg, xfer and buffers from multiple access */
int sel_sh;
int lsb;
};
static int max1111_read(struct device *dev, int channel)
{
struct max1111_data *data = dev_get_drvdata(dev);
uint8_t v1, v2;
int err;
/* writing to drvdata struct is not thread safe, wait on mutex */
mutex_lock(&data->drvdata_lock);
data->tx_buf[0] = (channel << data->sel_sh) |
MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
err = spi_sync(data->spi, &data->msg);
if (err < 0) {
dev_err(dev, "spi_sync failed with %d\n", err);
mutex_unlock(&data->drvdata_lock);
return err;
}
v1 = data->rx_buf[0];
v2 = data->rx_buf[1];
mutex_unlock(&data->drvdata_lock);
if ((v1 & 0xc0) || (v2 & 0x3f))
return -EINVAL;
return (v1 << 2) | (v2 >> 6);
}
#ifdef CONFIG_SHARPSL_PM
static struct max1111_data *the_max1111;
int max1111_read_channel(int channel)
{
return max1111_read(&the_max1111->spi->dev, channel);
}
EXPORT_SYMBOL(max1111_read_channel);
#endif
/*
* NOTE: SPI devices do not have a default 'name' attribute, which is
* likely to be used by hwmon applications to distinguish between
* different devices, explicitly add a name attribute here.
*/
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static ssize_t show_adc(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max1111_data *data = dev_get_drvdata(dev);
int channel = to_sensor_dev_attr(attr)->index;
int ret;
ret = max1111_read(dev, channel);
if (ret < 0)
return ret;
/*
* Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit
* sample. The LSB weight is 8mV or 16mV depending on the chip type.
*/
return sprintf(buf, "%d\n", ret * data->lsb);
}
#define MAX1111_ADC_ATTR(_id) \
SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static MAX1111_ADC_ATTR(0);
static MAX1111_ADC_ATTR(1);
static MAX1111_ADC_ATTR(2);
static MAX1111_ADC_ATTR(3);
static MAX1111_ADC_ATTR(4);
static MAX1111_ADC_ATTR(5);
static MAX1111_ADC_ATTR(6);
static MAX1111_ADC_ATTR(7);
static struct attribute *max1111_attributes[] = {
&dev_attr_name.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
NULL,
};
static const struct attribute_group max1111_attr_group = {
.attrs = max1111_attributes,
};
static struct attribute *max1110_attributes[] = {
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
NULL,
};
static const struct attribute_group max1110_attr_group = {
.attrs = max1110_attributes,
};
static int setup_transfer(struct max1111_data *data)
{
struct spi_message *m;
struct spi_transfer *x;
m = &data->msg;
x = &data->xfer[0];
spi_message_init(m);
x->tx_buf = &data->tx_buf[0];
x->len = MAX1111_TX_BUF_SIZE;
spi_message_add_tail(x, m);
x++;
x->rx_buf = &data->rx_buf[0];
x->len = MAX1111_RX_BUF_SIZE;
spi_message_add_tail(x, m);
return 0;
}
static int max1111_probe(struct spi_device *spi)
{
enum chips chip = spi_get_device_id(spi)->driver_data;
struct max1111_data *data;
int err;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
err = spi_setup(spi);
if (err < 0)
return err;
data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL);
if (data == NULL) {
dev_err(&spi->dev, "failed to allocate memory\n");
return -ENOMEM;
}
switch (chip) {
case max1110:
data->lsb = 8;
data->sel_sh = MAX1110_CTRL_SEL_SH;
break;
case max1111:
data->lsb = 8;
data->sel_sh = MAX1111_CTRL_SEL_SH;
break;
case max1112:
data->lsb = 16;
data->sel_sh = MAX1110_CTRL_SEL_SH;
break;
case max1113:
data->lsb = 16;
data->sel_sh = MAX1111_CTRL_SEL_SH;
break;
}
err = setup_transfer(data);
if (err)
return err;
mutex_init(&data->drvdata_lock);
data->spi = spi;
spi_set_drvdata(spi, data);
err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);
if (err) {
dev_err(&spi->dev, "failed to create attribute group\n");
return err;
}
if (chip == max1110 || chip == max1112) {
err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group);
if (err) {
dev_err(&spi->dev,
"failed to create extended attribute group\n");
goto err_remove;
}
}
data->hwmon_dev = hwmon_device_register(&spi->dev);
if (IS_ERR(data->hwmon_dev)) {
dev_err(&spi->dev, "failed to create hwmon device\n");
err = PTR_ERR(data->hwmon_dev);
goto err_remove;
}
#ifdef CONFIG_SHARPSL_PM
the_max1111 = data;
#endif
return 0;
err_remove:
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
return err;
}
static int max1111_remove(struct spi_device *spi)
{
struct max1111_data *data = spi_get_drvdata(spi);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
mutex_destroy(&data->drvdata_lock);
return 0;
}
static const struct spi_device_id max1111_ids[] = {
{ "max1110", max1110 },
{ "max1111", max1111 },
{ "max1112", max1112 },
{ "max1113", max1113 },
{ },
};
MODULE_DEVICE_TABLE(spi, max1111_ids);
static struct spi_driver max1111_driver = {
.driver = {
.name = "max1111",
.owner = THIS_MODULE,
},
.id_table = max1111_ids,
.probe = max1111_probe,
.remove = max1111_remove,
};
module_spi_driver(max1111_driver);
MODULE_AUTHOR("Eric Miao <eric.miao#marvell.com>");
MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver");
MODULE_LICENSE("GPL");

SPI device driver (max1111 in your case) get pointer to underlying SPI-controller (struct spi_device *spi) during probe stage (max1111_probe). Driver should use it send requests to controller (using spi_sync, for example). Driver doesn't know about what PINS SPI-controller use.
What SPI-controller is passed to SPI device driver? SPI device, should be
declared in the DTS-file inside SPI-controller node. The controller initialized from SPI-controller node is passed to device probe.
SPI-controller can be hardware (specific to SoC), or SPI-GPIO. In case of hardware SPI, pins usually dedicated and specified in SoC TRM. In case of SPI-GPIO, GPIO names are specified inside DTS-properties of SPI-GPIO. The properties names are: gpio-sck, gpio-miso, gpio-mosi, num-chipselects and cs-gpios (list).

The device tree file where the SPI device(max1111) is listed as child will have the corresponding base address of SPI module as shown in below example, this base address is related to the SPI bus not device.
spix#ABCDXYZ {
compatible = "busdriver,variant";
/*This naming convention depends on the device tree*/
mosi = <gpioA> /*replace gpioA/B/C/D with your gpios*/
miso = <gpioB>
gpio-clk = <gpioC>
gpio-cs0 = <gpioD>
spi-max-frequency = <freq1>;
;
;
;
max1111#0 {
compatible = "max1111";
reg = <0>;
spi-max-frequency = <freq2>;
;
;
;
};
;
;
;
}
In the above device tree file you can see the device node "max1111" listed as child under the bus node "spix" whose register address range starts at address #ABCDXYZ. You need to refer userguide of MCU you are using to know this base address.
The driver file max1111.c, is the device driver file for one of the SPI devices sitting on the spix bus.
The driver max1111.c is agnostic to the SPI lines that it is sitting on & just enjoys the APIs (Ex: data transfer & chip select handling) provided by the SPI bus driver associated with "busdriver,variant". The SPI bus driver will take care of configuring/multiplexing the MCU pins into SPI mode (handled explicitly in machine/board specific initialisation code).
So all you have to do is to,
Identify which SPI bus max1111 device is using.
Then go to the device tree file corresponding to you board.
Add your device driver details (+ SPI parameters) as part of child to that bus node
include your max1111.c into the kernel biuld directory (mostly drivers/spi folder)
that is it.
Note: here the assumption is your machine is already having that particular SPI bus driver enabled.

Related

Linux Driver Programming: Problem with usb_control_msg

Short Version
I want to write a Linux Driver for a custom USB device. Before writing the driver I used libusb-1.0 to test the device. With the following function call, I could read out a uin16_t value from the device:
status = libusb_control_transfer(handle, /* Device Handle */
0x80, /* bRequestType */
0x10, /* bRequest */
value, /* wValue */
0x0, /* wIndex */
((uint8_t *) &value), /* data */
2, /* wLength */
100); /* timeout */
After this call, I got a new value in the value variable.
Now I want to accomplish the same call in my Driver. I have tried the following in the probe function of my USB driver:
status = usb_control_msg(data->udev, usb_rcvctrlpipe(data->udev, 0), 0x10, USB_DIR_IN, 0, 0, (u8*) &my_data, 2, 100);
All I get is the return value -11 and on my device I don't see anything.
The only thing I am doing before this call, is calling data->udev = interface_to_usbdev(intf); to get the USB device from my interface.
Does anyone know, if I am missing something or if I am doing something wrong?
Long version
I want to learn how to write USB Drivers in Linux. As a DUT for which I can write a driver, I choose a Raspberry Pi Pico and the dev_lowlevel USB example. I adapt the code a little bit, so I can use a control transfer with bRequest 0x10 and bRequestType 0x0 (USB_DIR_OUT) to turn the Pico's onboard LED on or off and a control transfer with bRequest 0x10 and bRequestType 0x80 (USB_DIR_IN) to read back the current value of the LED.
With a user space program and the following code I can read out the value of the LED and turn it on or off:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <libusb-1.0/libusb.h>
#define VID 0x0000
#define DID 0x0001
int main(int argc, char **argv) {
int status, len;
libusb_device_handle *handle = NULL;
/* Init Libusb */
status = libusb_init(NULL);
if(status < 0) {
printf("Error init USB!\n");
return status;
}
handle = libusb_open_device_with_vid_pid(NULL, VID, DID);
if(!handle) {
printf("No device found with %04x:%04x\n", VID, DID);
libusb_exit(NULL);
return -1;
}
if(argc > 1)
value = atoi(argv[1]);
else {
/* Do control transfer */
status = libusb_control_transfer(handle, /* Device Handle */
0x80, /* bRequestType */
0x10, /* bRequest */
value, /* wValue */
0x0, /* wIndex */
((uint8_t *) &value), /* data */
2, /* wLength */
100); /* timeout */
if(status < 0) {
printf("Error during control transfer!\n");
libusb_close(handle);
libusb_exit(NULL);
return -1;
}
printf("Got: %d\n", value);
value = (value + 1) & 0x1;
}
/* Do control transfer */
status = libusb_control_transfer(handle, 0x0, 0x10, value, 0x0, NULL, 0, 100);
if(status < 0) {
printf("Error during control transfer!\n");
libusb_close(handle);
libusb_exit(NULL);
return -1;
}
libusb_close(handle);
libusb_exit(NULL);
return 0;
}
Now I want to control my device over a USB Driver. Here is what I got already:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/slab.h>
/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4 GNU/Linux");
MODULE_DESCRIPTION("Driver for my custom RPi Pico USB device");
struct pico_usb {
struct usb_device *udev;
};
#define PICO_VID 0x0000
#define PICO_PID 0x0001
static struct usb_device_id pico_usb_table [] = {
{ USB_DEVICE(PICO_VID, PICO_PID) },
{},
};
MODULE_DEVICE_TABLE(usb, pico_usb_table);
static int pico_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {
struct pico_usb *data;
int status;
int my_data;
printk("pico_usb_drv - Now I am in the Probe function!\n");
data = kzalloc(sizeof(struct pico_usb), GFP_KERNEL);
if(!data) {
printk("pico_usb_drv - Out of memory\n");
return -ENOMEM;
}
data->udev = interface_to_usbdev(intf);
usb_set_intfdata(intf, data);
/* Turn the LED on */
status = usb_control_msg(data->udev, usb_sndctrlpipe(data->udev, 0), 0x10, USB_DIR_OUT, 1, 0, 0, 0, 100);
/* Read LED state */
printk("pico_usb_drv - status USB_DIR_OUT: %d\n", status);
status = usb_control_msg(data->udev, usb_rcvctrlpipe(data->udev, 0), 0x10, USB_DIR_IN, 0, 0, (u8*) &my_data, 2, 100);
printk("pico_usb_drv - status USB_DIR_IN: %d\n", status);
return 0;
}
static void pico_usb_disconnect(struct usb_interface *intf) {
struct pico_usb *data;
printk("pico_usb_drv - Now I am in the Disconnect function!\n");
data = usb_get_intfdata(intf);
kfree(data);
}
static struct usb_driver pico_usb_driver = {
//.owner = THIS_MODULE,
.name = "pico_usb",
.id_table = pico_usb_table,
.probe = pico_usb_probe,
.disconnect = pico_usb_disconnect,
};
/**
* #brief This function is called, when the module is loaded into the kernel
*/
static int __init pico_usb_init(void) {
int result;
printk("pico_usb_drv - Registering the PICO USB device\n");
result = usb_register(&pico_usb_driver);
if(result) {
printk("pico_usb_drv - Error registering the PICO USB device\n");
return -result;
}
return 0;
}
/**
* #brief This function is called, when the module is removed from the kernel
*/
static void __exit pcio_usb_exit(void) {
printk("pico_usb_drv - Unregistering the PICO USB device\n");
usb_deregister(&pico_usb_driver);
}
module_init(pico_usb_init);
module_exit(pcio_usb_exit);
The first control message works and my LED is turned on. But the second control message doesn't do anything, but gives me the error code -11 back.
Does anyone know, if I am missing something or if I am doing something wrong?
Ok, I found the solution. Instead of usb_control_msg I use usb_control_msg_recv now and everything works just fine.
usb_control_msg_recv takes one more argument:
int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *driver_data, __u16 size, int timeout, gfp_t memflags)
As I pass the pointer to a variable and don't want to allocate memory dynamically, I set the memflags argument to 0.

How are pins defined for sensors in embedded C?

I have a Nordic nRF52840DK board that needs to be connected to an LSM6DSL accelerometer. I am using the Zephyr RTOS and the sample code from Zephyr is shown below:
/*
* Copyright (c) 2018 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/sensor.h>
#include <stdio.h>
#include <sys/util.h>
static inline float out_ev(struct sensor_value *val)
{
return (val->val1 + (float)val->val2 / 1000000);
}
static int print_samples;
static int lsm6dsl_trig_cnt;
static struct sensor_value accel_x_out, accel_y_out, accel_z_out;
static struct sensor_value gyro_x_out, gyro_y_out, gyro_z_out;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x_out, magn_y_out, magn_z_out;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press_out, temp_out;
#endif
#ifdef CONFIG_LSM6DSL_TRIGGER
static void lsm6dsl_trigger_handler(const struct device *dev,
struct sensor_trigger *trig)
{
static struct sensor_value accel_x, accel_y, accel_z;
static struct sensor_value gyro_x, gyro_y, gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x, magn_y, magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press, temp;
#endif
lsm6dsl_trig_cnt++;
sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &accel_x);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &accel_y);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &accel_z);
/* lsm6dsl gyro */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &gyro_x);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &gyro_y);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &gyro_z);
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
/* lsm6dsl external magn */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_MAGN_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &magn_x);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &magn_y);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &magn_z);
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
/* lsm6dsl external press/temp */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_PRESS);
sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);
sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
#endif
if (print_samples) {
print_samples = 0;
accel_x_out = accel_x;
accel_y_out = accel_y;
accel_z_out = accel_z;
gyro_x_out = gyro_x;
gyro_y_out = gyro_y;
gyro_z_out = gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
magn_x_out = magn_x;
magn_y_out = magn_y;
magn_z_out = magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
press_out = press;
temp_out = temp;
#endif
}
}
#endif
void main(void)
{
int cnt = 0;
char out_str[64];
struct sensor_value odr_attr;
const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));
if (lsm6dsl_dev == NULL) {
printk("Could not get LSM6DSL device\n");
return;
}
/* set accel/gyro sampling frequency to 104 Hz */
odr_attr.val1 = 104;
odr_attr.val2 = 0;
if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for accelerometer.\n");
return;
}
if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_GYRO_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for gyro.\n");
return;
}
#ifdef CONFIG_LSM6DSL_TRIGGER
struct sensor_trigger trig;
trig.type = SENSOR_TRIG_DATA_READY;
trig.chan = SENSOR_CHAN_ACCEL_XYZ;
if (sensor_trigger_set(lsm6dsl_dev, &trig, lsm6dsl_trigger_handler) != 0) {
printk("Could not set sensor type and channel\n");
return;
}
#endif
if (sensor_sample_fetch(lsm6dsl_dev) < 0) {
printk("Sensor sample update error\n");
return;
}
while (1) {
/* Erase previous */
printk("\0033\014");
printf("LSM6DSL sensor samples:\n\n");
/* lsm6dsl accel */
sprintf(out_str, "accel x:%f ms/2 y:%f ms/2 z:%f ms/2",
out_ev(&accel_x_out),
out_ev(&accel_y_out),
out_ev(&accel_z_out));
printk("%s\n", out_str);
/* lsm6dsl gyro */
sprintf(out_str, "gyro x:%f dps y:%f dps z:%f dps",
out_ev(&gyro_x_out),
out_ev(&gyro_y_out),
out_ev(&gyro_z_out));
printk("%s\n", out_str);
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
/* lsm6dsl external magn */
sprintf(out_str, "magn x:%f gauss y:%f gauss z:%f gauss",
out_ev(&magn_x_out),
out_ev(&magn_y_out),
out_ev(&magn_z_out));
printk("%s\n", out_str);
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
/* lsm6dsl external press/temp */
sprintf(out_str, "press: %f kPa - temp: %f deg",
out_ev(&press_out), out_ev(&temp_out));
printk("%s\n", out_str);
#endif
printk("loop:%d trig_cnt:%d\n\n", ++cnt, lsm6dsl_trig_cnt);
print_samples = 1;
k_sleep(K_MSEC(2000));
}
}
From my previous experience working with Arduino, you need to define the pins before the main function, but there isn't any definition on pins in this code example. So my question is: How does the board know which pins to use when connected to the sensor?
Thanks in advance!
In embedded C (bare metal and some RTOS), the developer is expected to know what is connected where and which registers to use to access the connected hardware by driving the right pins.
In the specific case of Zephyr (as well as Das U-Boot and Linux on some architectures), the system configuration (among other things pins, but not only) is described by a device-tree. It is a "a data structure for describing hardware", which allows the firmware code to become independent of the actual hardware.
The device-tree will describe many things, the most important being:
the device's memory map
the list of CPUs and their properties (frequency, caches, ...)
the peripherals, as well as their configuration
Zephyr does have a very good introduction to device-tree.
In the code you posted, Zephyr does retrieve the device information from the device-tree when executing the following line:
const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));

ARM: I2C address write waits idefenitly since I2C flag isn't set after receiving ACK

I am developing a bare-metal ARM project using LPC2138. I2C is configured for master transmitter mode. I have used the following code for writing data to the slave. I have chosen I2C polling method.
#define AA (0x04)
#define SI (0x08)
#define STO (0x10)
#define STA (0x20)
#define I2EN (0x40)
/* I2C0CONCLR bits */
#define AAC AA
#define SIC SI
#define STAC STA
#define I2ENC I2EN
void I2C0_init(unsigned char mode)
{
PINSEL0 = 0x50;
IO0DIR = 0x0C;
IO0SET = 0x0C;
I2C0CONCLR = AAC | SIC | STAC | I2ENC;
I2C0SCLL = 0x4B;
I2C0SCLH = 0x4B;
I2C0CONSET = I2EN;
__i2c0_mode = mode;
__i2c0_initialised = TRUE;
}
static bool I2C0_start()
{
I2C0CONSET = STA;
while(!(I2C0CONSET & SI));
if(I2C0STAT != 0x08)
{
return FALSE;
}
return TRUE;
}
static inline bool I2C0_slave_addr(unsigned char addr, bool rw)
{
addr = rw ? addr | BIT_MASK_RD : addr & BIT_MASK_WR;
I2C0DAT = addr;
I2C0CONCLR = SIC | STAC;
while(!(I2C0CONSET & SI)); // infinite loop here
I2C0CONCLR = SIC;
if(rw == I2C_WR && I2C0STAT != 0x18)
return FALSE;
else if(rw == I2C_RD && I2C0STAT != 0x40)
return FALSE;
return TRUE;
}
long I2C0_write(unsigned char addr, const char *buff, unsigned long buff_len)
{
unsigned long count = 0;
if(!__i2c0_initialised)
return I2C_ERROR_UNINITIALISED;
if(!buff_len)
return 0;
if(__i2c0_mode == I2C_MODE_MASTER)
{
I2C0_start();
I2C0_slave_addr(addr, I2C_WR);
..............................
..............................
}
}
I actually simulated the code using Proteus. I2C start condition and slave address are being sent properly and the address is successfully acknowledged by the slave.
After the slave acknowledgement, the SI flag should be set again automatically as per the LPC2138 reference manual. This is not happening and because of that, the code enters into an infinite loop.
I don't understand where I went wrong. Can someone please help?

STM32 USB OTG HOST Library hangs trying to create file with FatFs

I am trying to create a file with FatFs on USB flash, but my f_open call trying to read boot sector for first time file system mount hangs on this function.
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
BYTE status = USBH_MSC_OK;
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if(HCD_IsDeviceConnected(&USB_OTG_Core))
{
do
{
status = USBH_MSC_Read10(&USB_OTG_Core, buff,sector,512 * count);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
return RES_ERROR;
}
}
while(status == USBH_MSC_BUSY ); // Loop which create hanging state
}
if(status == USBH_MSC_OK)
return RES_OK;
return RES_ERROR;
}
The main problem is the loop which creates hanging state
while(status == USBH_MSC_BUSY );
So I do not know what to do to avoid this. Using debugger I discover that state is caused by parameter CmdStateMachine of structure USBH_MSC_BOTXferParam, type USBH_BOTXfer_TypeDef is equal CMD_UNINITIALIZED_STATE which actually cause miss up of switch statement of USBH_MSC_Read10 function.
/**
* #brief USBH_MSC_Read10
* Issue the read command to the device. Once the response received,
* it updates the status to upper layer
* #param dataBuffer : DataBuffer will contain the data to be read
* #param address : Address from which the data will be read
* #param nbOfbytes : NbOfbytes to be read
* #retval Status
*/
uint8_t USBH_MSC_Read10(USB_OTG_CORE_HANDLE *pdev,
uint8_t *dataBuffer,
uint32_t address,
uint32_t nbOfbytes)
{
uint8_t index;
static USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
uint16_t nbOfPages;
status = USBH_MSC_BUSY;
if(HCD_IsDeviceConnected(pdev))
{
switch(USBH_MSC_BOTXferParam.CmdStateMachine)
{
case CMD_SEND_STATE:
/*Prepare the CBW and relevant field*/
USBH_MSC_CBWData.field.CBWTransferLength = nbOfbytes;
USBH_MSC_CBWData.field.CBWFlags = USB_EP_DIR_IN;
USBH_MSC_CBWData.field.CBWLength = CBW_LENGTH;
USBH_MSC_BOTXferParam.pRxTxBuff = dataBuffer;
for(index = CBW_CB_LENGTH; index != 0; index--)
{
USBH_MSC_CBWData.field.CBWCB[index] = 0x00;
}
USBH_MSC_CBWData.field.CBWCB[0] = OPCODE_READ10;
/*logical block address*/
USBH_MSC_CBWData.field.CBWCB[2] = (((uint8_t*)&address)[3]);
USBH_MSC_CBWData.field.CBWCB[3] = (((uint8_t*)&address)[2]);
USBH_MSC_CBWData.field.CBWCB[4] = (((uint8_t*)&address)[1]);
USBH_MSC_CBWData.field.CBWCB[5] = (((uint8_t*)&address)[0]);
/*USBH_MSC_PAGE_LENGTH = 512*/
nbOfPages = nbOfbytes/ USBH_MSC_PAGE_LENGTH;
/*Tranfer length */
USBH_MSC_CBWData.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]) ;
USBH_MSC_CBWData.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]) ;
USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SEND_CBW;
/* Start the transfer, then let the state machine
manage the other transactions */
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_USB_TRANSFERS;
USBH_MSC_BOTXferParam.BOTXferStatus = USBH_MSC_BUSY;
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_WAIT_STATUS;
status = USBH_MSC_BUSY;
break;
case CMD_WAIT_STATUS:
if((USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_OK) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Commands successfully sent and Response Received */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_OK;
}
else if (( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_FAIL ) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
}
else if ( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_PHASE_ERROR )
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_PHASE_ERROR;
}
else
{
/* Wait for the Commands to get Completed */
/* NO Change in state Machine */
}
break;
default:
break;
}
}
return status;
}
Here is USBH_BOTXfer_TypeDef type declaration;
typedef struct _BOTXfer
{
uint8_t MSCState;
uint8_t MSCStateBkp;
uint8_t MSCStateCurrent;
uint8_t CmdStateMachine;
uint8_t BOTState;
uint8_t BOTStateBkp;
uint8_t* pRxTxBuff;
uint16_t DataLength;
uint8_t BOTXferErrorCount;
uint8_t BOTXferStatus;
} USBH_BOTXfer_TypeDef;
During the debug I discover that all fields of it is 0x00.
Here are my FatFs calls
int main(void)
{
FATFS Fat;
FIL file;
FRESULT fr;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
/* Enable SWO output */
DBGMCU->CR = 0x00000020;
GPIOD->MODER=0x55000000;
GPIOD->OTYPER = 0x00000000;
GPIOD->OSPEEDR = 0x00000001;
while(1)
{
if (!USB_MSC_IsInitialized())
{
USB_MSC_Initialize();
}
if (USB_MSC_IsConnected())
{
GPIOD->ODR = (1 << 15);
disk_initialize(0);
fr = f_mount(0, &Fat);
if(fr == FR_OK)
{
fr = f_open(&file,"0:DP_lab8.pdf",(FA_CREATE_ALWAYS | FA_WRITE));
if (fr == FR_OK)
{
f_close(&file);
}
f_mount(0, NULL);
}
}
else
{
GPIOD->ODR = (1 << 14);
}
USB_MSC_Main();
}
}
USB_MSC_IsConnected function is:
int USB_MSC_IsConnected(void)
{
if (g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED)
{
USB_MSC_Uninitialize();
}
return !(g_USB_MSC_HostStatus == USB_DEV_DETACHED ||
g_USB_MSC_HostStatus == USB_HOST_NO_INIT ||
g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED);
}
And device states are:
typedef enum
{
USB_HOST_NO_INIT = 0, /* USB interface not initialized */
USB_DEV_DETACHED, /* no device connected */
USB_SPEED_ERROR, /* unsupported USB speed */
USB_DEV_NOT_SUPPORTED, /* unsupported device */
USB_DEV_WRITE_PROTECT, /* device is write protected */
USB_OVER_CURRENT, /* overcurrent detected */
USB_DEV_CONNECTED /* device connected and ready */
} USB_HostStatus;
The value of g_USB_MSC_HostStatus is received by standard USB HOST user callbacks.
I think it is a bug in ST host library. I've hunted it down, as my usb host was unable to pass enumeration stage. After a fix the stack is Ok.
There is union _USB_Setup in usbh_def.h file in "STM32Cube/Repository/STM32Cube_FW_F7_V1.13.0/Middlewares/ST/STM32_USB_Host_Library/Core/Inc" (any chip, not only F7, any version, not only V1.13.0). It has uint16_t bmRequestType and uint16_t bRequest. These two fileds must be uint8_t as of USB specs. Fixing this issue made usb host go as needed. Enumeration stage passes ok, and all other stages as well.

How can I use the USB DMA Engine for the LPC1788?

I'm developing code for the NXP LPC1788 microcontroller and lately I've been trying to improve the way that the USB works. My current issue with the USB is that it's configured in slave mode and, due to the high volume of messages that have to be sent and received in normal operation, the CPU spends most of its time handling USB which creates a bottleneck.
I've been trying to resolve this issue by switching from slave-mode configuration to DMA-mode. I've been using example projects to help and I think most of the code I need is in place.
USB initialisation works fine, as before. The problem is that as soon as I try to send a USB message to an endpoint that I configure in DMA-mode (by enabling the appropriate bit in the EpDMAEn register), I get a USB System Error Interrupt for that endpoint. The only information I can get about this is that:
If a system error (AHB bus error) occurs when transferring the data or when fetching or
updating the DD the corresponding bit is set in this register. SysErrIntSt is a read-only
register.
At this point in the program, I haven't touched the UDCA or setup any DMA descriptors or anything like that past initialisation. This is an error that occurs as soon as the first message is received on the USB bus before I need to do any of that, I believe.
I'm using endpoints 2 IN and OUT, which are double-buffered bulk endpoints with a maximum packet size of 64 bytes.
I've confirmed that the USB works fine if I don't use the DMA.
I've confirmed that the USB works fine if I go through the process of initialising the DMA engine but configure the endpoint in slave-mode instead of DMA-mode.
I've confirmed that the USB Mass Storage example under Example Projects -> NXP -> LP17xx -> 177x_8x CMSIS works fine if I use its default configuration:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000000
...
but also breaks in the same way if I change this to:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000030 /* Endpoint 2 IN and OUT */
...
At the beginning of the USB hardware source file, I put the following:
#ifdef USB_DMA
// Stores information received using DMA on OUT endpoints.
//uint8_t dataBufferOUT[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferOUT = (uint8_t*)DMA_BUF_ADR;
// Stores information transferred using DMA on IN endpoints.
//uint8_t dataBufferIN[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferIN = (uint8_t*)(DMA_BUF_ADR+DD_BUFFER_SIZE*
USB_MAX_PACKET_SIZE);
// Current dataBufferOUT index;
uint16_t dataOUT;
// Current dataBufferIN index.
uint16_t dataIN;
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
I initialise the USB peripheral with this code:
void USBInit()
{
// Configure USB pins.
PINSEL_ConfigPin(0, 29, 1); // USB_D+1
PINSEL_ConfigPin(0, 30, 1); // USB_D-1
PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
PINSEL_ConfigPin(2, 9, 1); // USB_CONNECT1
PINSEL_ConfigPin(1, 30, 2); // USB_VBUS
// Turn on power and clock
CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);
//PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);
// Set DEV_CLK_EN and AHB_CLK_EN.
LPC_USB->USBClkCtrl |= 0x12;
// Wait until change is reflected in clock status register.
while((LPC_USB->USBClkSt & 0x12) != 0x12);
// Enable NVIC USB interrupts.
NVIC_EnableIRQ(USB_IRQn);
// Reset the USB.
USBReset();
// Set device address to 0x0 and enable device & connection.
USBSetAddress(0);
// TEMP.
sendMessageFlag = 0;
#ifdef USB_DMA
dataIN = 0;
dataOUT = 0;
#endif
}
My USB reset code:
void USBReset()
{
LPC_USB->EpInd = 0;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
LPC_USB->EpInd = 1;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);
LPC_USB->EpIntClr = 0xFFFFFFFF;
#ifdef USB_DMA
LPC_USB->EpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;
#else
LPC_USB->EpIntEn = 0xFFFFFFFF;
#endif
LPC_USB->DevIntClr = 0xFFFFFFFF;
LPC_USB->DevIntEn = DEV_STAT_INT | EP_SLOW_INT /*| EP_FAST_INT*/ ;
#ifdef USB_DMA
uint32_t n;
LPC_USB->UDCAH = USB_RAM_ADR;
LPC_USB->DMARClr = 0xFFFFFFFF;
LPC_USB->EpDMADis = 0xFFFFFFFF;
LPC_USB->EpDMAEn = USB_DMA_EP;
LPC_USB->EoTIntClr = 0xFFFFFFFF;
LPC_USB->NDDRIntClr = 0xFFFFFFFF;
LPC_USB->SysErrIntClr = 0xFFFFFFFF;
LPC_USB->DMAIntEn = 0x00000007;
DDMemMap[0] = 0x00000000;
DDMemMap[1] = 0x00000000;
for (n = 0; n < USB_EP_NUM; n++) {
udca[n] = 0;
UDCA[n] = 0;
}
#endif
}
When ready, this is used to run the USB:
void USBRun()
{
USBSetConnection(TRUE);
}
Finally, my USB interrupt routine:
void USB_IRQHandler(void)
{
OS_EnterInterrupt();
uint32_t data, val, pIndex, lIndex, currEpisr;
uint32_t interruptData = LPC_USB->DevIntSt;
#ifdef USB_DMA
uint32_t dmaInterruptData = LPC_USB->DMAIntSt;
#endif
//printf("InterruptData: 0x%x\n", interruptData);
if (interruptData & ERR_INT)
{
writeSIECommand(CMD_RD_ERR_STAT);
data = readSIECommandData(DAT_RD_ERR_STAT);
// printf("Error data: 0x%x\n", data);
//getchar();
}
// Handle device status interrupt (reset, connection change, suspend/resume).
if(interruptData & DEV_STAT_INT)
{
LPC_USB->DevIntClr = DEV_STAT_INT;
writeSIECommand(CMD_GET_DEV_STAT);
data = readSIECommandData(DAT_GET_DEV_STAT);
//printf("Data: 0x%x\n", data);
// Device reset.
if(data & DEV_RST)
{
USBReset();
USBResetCore();
//printf("USB Reset\n");
}
// Connection change.
if(data & DEV_CON_CH)
{
//printf("Connection change\n");
/* Pass */
}
// Suspend/resume.
if(data & DEV_SUS_CH)
{
if(data & DEV_SUS)
{
//printf("USB Suspend\n");
USBSuspend();
}
else
{
//printf("USB Resume\n");
USBResume();
}
}
OS_LeaveInterrupt();
return;
}
// Handle endpoint interrupt.
if(interruptData & EP_SLOW_INT)
{
//printf("Endpoint slow\n");
data = LPC_USB->EpIntSt;
//printf("EP interrupt: 0x%x\n", data);
currEpisr = 0;
for(pIndex=0; pIndex < USB_EP_NUM; pIndex++)
{
lIndex = pIndex >> 1;
if(data == currEpisr) break;
if(data & (1 << pIndex))
{
currEpisr |= (1 << pIndex);
LPC_USB->EpIntClr = 1 << pIndex;
while((LPC_USB->DevIntSt & CDFULL_INT) == 0);
val = LPC_USB->CmdData;
// OUT endpoint.
if((pIndex & 1) == 0)
{
// Control OUT endpoint.
if(pIndex == 0)
{
// Setup Packet.
if(val & EP_SEL_STP)
{
if(USB_P_EP[0])
{
USB_P_EP[0](USB_EVT_SETUP);
continue;
}
}
}
if(USB_P_EP[lIndex])
{
USB_P_EP[lIndex](USB_EVT_OUT);
}
}
// IN endpoint.
else
{
if(USB_P_EP[lIndex])
{
if(lIndex > 0) clearSendMessageFlag(lIndex);
USB_P_EP[lIndex](USB_EVT_IN);
}
}
}
}
LPC_USB->DevIntClr = EP_SLOW_INT;
}
#ifdef USB_DMA
if (dmaInterruptData & 0x00000001) { /* End of Transfer Interrupt */
data = LPC_USB->EoTIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_EOT);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_EOT);
}
}
}
}
LPC_USB->EoTIntClr = data;
}
if (dmaInterruptData & 0x00000002) { /* New DD Request Interrupt */
data = LPC_USB->NDDRIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_NDR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_NDR);
}
}
}
}
LPC_USB->NDDRIntClr = data;
}
if (dmaInterruptData & 0x00000004) { /* System Error Interrupt */
data = LPC_USB->SysErrIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_ERR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_ERR);
}
}
}
}
LPC_USB->SysErrIntClr = data;
}
#endif /* USB_DMA */
OS_LeaveInterrupt();
}
What I'm ideally looking for is a solution if you can spot an error in any of my code or a working example program that can be run on an LPC1788 that demonstrates USB message transmission and reception using the DMA engine.
I'd also appreciate any information on what can cause an AHB bus error.
EDIT
In response to Turbo J's answer below:
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.
In my USB hardware header file, I have:
/* USB RAM Definitions */
#define USB_RAM_ADR LPC_PERI_RAM_BASE /* USB RAM Start Address */
#define USB_RAM_SZ 0x00004000 /* USB RAM Size (4kB) */
LPC_PERI_RAM_BASE has the value 0x20000000UL.
In my source file, I have:
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
Where USB_EP_NUM is 32.
Therefore, UDCA should be a 128-byte array that begins at the start of the RAM memory block, I believe.
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.

Resources