Maxim
Пункты: 722
Регистрация: 14.12.2013
Здравствуйте.
Подключил к плате SPI ADC на 4 канала. После выполнения преобразования из АЦП необходимо забирать 96 бит (4 канала по 24 бита). Передачу данных планировалось сделать с минимальным использованием прерываний, у ECSPI есть аппаратные возможности для организации обмена. В модуле ECSPI у imx53 длину посылки можно выбирать с помощью BURST LENGTH и запускать передачу по сигналу SPI_RDY.
Но на форумах пишут что при длине посылки более 32 бит модуль начинает работать непредсказуемо:
https://community.freescale.com/thread/308673.
Кто-нибудь сталкивался с такой проблемой?
lexx666
Пункты: 11780
Регистрация: 28.07.2011
Пол: Мужчина
Из: Барнаул
Задаете длину слова 16 бит. Вычитываете необходимое количество байт используя стандартный linux драйвер - spidev.
Указанное количество байт вычитывается за раз, за один чип селект (cs).
В чём именно проблема ? Ссылка не работает у меня к сожелению...
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
На любой вопрос есть любой ответ.
Maxim
Пункты: 722
Регистрация: 14.12.2013
Для работы с внешним АЦП ads1274 был написан модуль.
АЦП имеет 4 независимых канала, данные передаются по SPI. У АЦП есть выход RDY, когда данные готовы уровень меняется с 1 на 0 и возвращается в 1 после начала чтения по spi или после завершения следующего преобразования. Данные необходимо забирать до завершения следующего преобразования, иначе они теряются. Для чтения используется прерывание по фронту gpio. АЦП выдает данные в виде последовательности из 96 бит (4 канала по 24 бита). Модуль накапливает данные в памяти. Приложение читает их через файл устройства.
1. При минимальной загрузке процессора пропусков в приеме данных нет, но с увеличением нагрузки начинают появляться пропуски. (при запуске qt demo или при выводе файла cat test.txt) Есть ли способ добиться стабильной работы модуля при большой нагрузке на процессор?
2. У spi imx53 есть вход spi rdy. Этот вход может управлять началом передачи данных по spi. При этом длина посылки может быть от 1 до 4095 бит. Но при увеличении длины посылки более 32 бит, отправляется только 32 бита. Работает ли этот режим для посылок длиннее 32 бит?
Код модуля (модуль читает только 32 бита из 96)
Код #include <linux/kernel.h> /* Для printk() и т.д. */
#include <linux/module.h> /* Эта частичка древней магии, которая оживляет модули */
#include <linux/init.h> /* Определения макросов */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* put_user */
#include <asm/io.h> /* ioremap_nocache */
#include <linux/ioport.h> /* struct resource */
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "ADS1274.h"
// Ниже мы задаём информацию о модуле, которую можно будет увидеть с помощью Modinfo
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Author" );
MODULE_DESCRIPTION( "ADC Driver" );
MODULE_SUPPORTED_DEVICE( "ads1274" ); /* /dev/testdevice */
//-------------------------------------------------------------
#define ADC_DRDY_PIN 154
#define ADC_SYNC_PIN 91
#define ADC_TEST_PIN 84
#define SUCCESS 0
#define DEVICE_NAME "ads1274" /* Имя нашего устройства */
#define INPUT_BUFFER_SIZE 21000
// Поддерживаемые нашим устройством операции
static int device_open( struct inode *, struct file * );
static int device_release( struct inode *, struct file * );
static ssize_t device_read( struct file *, char *, size_t, loff_t * );
static ssize_t device_write( struct file *, const char *, size_t, loff_t * );
// Глобальные переменные, объявлены как static, воизбежание конфликтов имен.
static dev_t first; // Global variable for the first device number
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class
//static int major_number; /* Старший номер устройства нашего драйвера */
static int is_device_open = 0; /* Используется ли девайс ? */
static int status_reg;
//static int test_data_cnt = 0;
//int testVrVar;
static int region_size = 256;
static void *ccm_base;
static void *pwm1_base;
static void *ecspi1_base;
struct
{
int dataA[(INPUT_BUFFER_SIZE+3)];
int dataB[(INPUT_BUFFER_SIZE+3)];
unsigned char busy_buff;
int start;
int end;
} inputBuffer;
// Прописываем обработчики операций на устройством
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static irqreturn_t ads1274_interrupt(int irq, void *dev_id)
{
status_reg = *(int*)(ecspi1_base+ECSPI_STATREG);
status_reg &= 0x00000008;
while(status_reg == 0x08)
{
status_reg = *(int*)(ecspi1_base+ECSPI_STATREG);
status_reg &= 0x00000008;
if (status_reg == 0x00000008)
{
if (inputBuffer.busy_buff == 1)
{
inputBuffer.dataB[inputBuffer.end] = *(int*)(ecspi1_base+ECSPI_RXDATA);
inputBuffer.end++;
if (inputBuffer.end >= INPUT_BUFFER_SIZE)
{
inputBuffer.busy_buff = 0;
inputBuffer.end = 0;
}
}
else
{
inputBuffer.dataA[inputBuffer.end] = *(int*)(ecspi1_base+ECSPI_RXDATA);
inputBuffer.end++;
if (inputBuffer.end >= INPUT_BUFFER_SIZE)
{
inputBuffer.busy_buff = 1;
inputBuffer.end = 0;
}
}
}
}
*(int*)(ecspi1_base+ECSPI_TXDATA) = 0x00000000;
return IRQ_HANDLED;
}
static void ResetInputBuffer(void)
{
inputBuffer.busy_buff = 0;
inputBuffer.start=0;
inputBuffer.end=0;
}
static int gpio_irq_init(void)
{
int ret;
ret = gpio_request(ADC_DRDY_PIN, "gpio154"); //GPIO_5_26
if (ret < 0)
{
printk("ADS1274 driver: can't get GPIO pin: %d\n", ADC_DRDY_PIN);
return ret;
}
printk("GPIO pin: %d\n", ADC_DRDY_PIN);
gpio_direction_input(ADC_DRDY_PIN);
set_irq_type(gpio_to_irq(ADC_DRDY_PIN), IRQ_TYPE_EDGE_FALLING);
ret = request_irq(gpio_to_irq(ADC_DRDY_PIN), ads1274_interrupt, IRQF_TRIGGER_FALLING, "adc_drdy_int", NULL);
if (ret < 0)
{
printk("ADS1274 driver: IRQ request failed: %d\n", ret);
return ret;
}
return SUCCESS;
}
// Функция загрузки модуля. Входная точка. Можем считать что это наш main()
static int __init ads1274_init( void )
{
int gpio3_ret;
unsigned int wait_del;
int gpio_test_ret;
int gpio_int_status;
struct resource *res;
printk( KERN_ALERT "ADS1274 driver loaded!\n" );
ResetInputBuffer();
res = request_mem_region(CCM_BASE, region_size, "ccm");
ccm_base = ioremap_nocache(CCM_BASE, region_size);
res = request_mem_region(PWM1_BASE, region_size, "pwm1");
pwm1_base = ioremap_nocache(PWM1_BASE, region_size);
res = request_mem_region(ECSPI1_BASE, region_size, "ecspi1");
ecspi1_base = ioremap_nocache(ECSPI1_BASE, region_size);
*(int*)(ccm_base+CCM_CCGR2) |= 0xC00; // ipg_clk
*(int*)(ccm_base+CCM_CCGR4) |= 0x3C0000;
*(int*)(pwm1_base+PWM_PWMCR) = 0; // Disable PWM
*(int*)(pwm1_base+PWM_PWMCR) = 0x3C10000;
*(int*)(pwm1_base+PWM_PWMSR) = 0;
*(int*)(pwm1_base+PWM_PWMIR) = 0;
*(int*)(pwm1_base+PWM_PWMSAR) = 2;
*(int*)(pwm1_base+PWM_PWMPR) = 3;
*(int*)(pwm1_base+PWM_PWMCR) |= 1; // Enable PWM
*(int*)(ecspi1_base+ECSPI_CONREG) = 0; /* Disable ECSPI */
*(int*)(ecspi1_base+ECSPI_CONREG) = 0x017010F8;
*(int*)(ecspi1_base+ECSPI_CONFIGREG) = 0x00FF0FFF;
*(int*)(ecspi1_base+ECSPI_INTREG) = 0; /* Rx Int Enabled */
*(int*)(ecspi1_base+ECSPI_DMAREG) = 0; /* DMA Disabled, 45 elements in RxFIFO */
*(int*)(ecspi1_base+ECSPI_PERIODREG) = 0x00000000; /* Delay Disabled */
*(int*)(ecspi1_base+ECSPI_TESTREG) = 0; /* Test Disabled */
*(int*)(ecspi1_base+ECSPI_CONREG) |= 1; /* Enable ECSPI */
gpio_test_ret = gpio_request(ADC_TEST_PIN, "gpio84"); //GPIO_3_20
if (gpio_test_ret < 0)
{
printk("TSTDRV: can't get GPIO TEST pin: %d\n", ADC_TEST_PIN);
return gpio_test_ret;
}
printk("GPIO TEST PIN: %d\n", ADC_TEST_PIN);
gpio_direction_output(ADC_TEST_PIN,0);
gpio_set_value(ADC_TEST_PIN, 1);
gpio3_ret = gpio_request(ADC_SYNC_PIN, "gpio91"); //ADC_SYNC - GPIO_3_27
if (gpio3_ret < 0)
{
printk("TSTDRV: can't get GPIO RDY pin: %d\n", ADC_SYNC_PIN);
return gpio3_ret;
}
printk("GPIO RDY PIN: %d\n", ADC_SYNC_PIN);
gpio_direction_output(ADC_SYNC_PIN,0);
for (wait_del = 0; wait_del < 2000; wait_del++); /* Wait ADC Reset */
gpio_set_value(ADC_SYNC_PIN, 1);
// int alloc_chrdev_region(dev_t *first, unsigned int firstminor, unsigned int cnt, char *name);
// динамически определяется свободный старший номер и регистрируется число cnt(1) среди номеров файлов устройств, начинающиеся с <the free major, firstminor>,
// с заданным именем файла name.
if (alloc_chrdev_region(&first, 0, 1, "adc1274driver") < 0)
{
return -1;
}
// Класс устройства создается следующим образом:
// struct class *cl = class_create(THIS_MODULE, "<device class name>");
if ((cl = class_create(THIS_MODULE, "adc")) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
// Затем в этот класс информация об устройстве (<major, minor>) заносится следующим образом:
// device_create(cl, NULL, first, NULL, "<device name format>", ...);
if (device_create(cl, NULL, first, NULL, "ads1274") == NULL)
{
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
// занесем нужные нам файловые операции (my_open, my_close, my_read, my_write, …) в структуру,
// описывающую файловые операции (struct file_operations pugs_fops)
// и ею инициализируем структуру, описывающую символьное устройство (struct cdev c_dev);
// используем для этого обращение cdev_init().
cdev_init(&c_dev, &fops);
// Затем передадим эту структуру в VFS с помощью вызова cdev_add()
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
printk( "ADS1274 module is loaded!\n" );
gpio_int_status = gpio_irq_init();
return gpio_int_status;
}
// Функция выгрузки модуля
static void __exit ads1274_exit( void )
{
// Освобождаем устройство
free_irq(gpio_to_irq(ADC_DRDY_PIN), NULL);
cdev_del(&c_dev);
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
release_mem_region(CCM_BASE, region_size);
release_mem_region(PWM1_BASE, region_size);
release_mem_region(ECSPI1_BASE, region_size);
iounmap(ccm_base);
iounmap(pwm1_base);
iounmap(ecspi1_base);
printk( KERN_ALERT "ADS1274 module is unloaded!\n" );
}
// Указываем наши функции загрузки и выгрузки
module_init( ads1274_init );
module_exit( ads1274_exit );
static int device_open( struct inode *inode, struct file *file )
{
if ( is_device_open ) return -EBUSY;
is_device_open++;
return SUCCESS;
}
static int device_release( struct inode *inode, struct file *file )
{
is_device_open--;
return SUCCESS;
}
static ssize_t device_write( struct file *filp, const char *buff, size_t len, loff_t * off )
{
printk( "Sorry, this operation isn't supported.\n" );
return -EINVAL;
}
static ssize_t device_read( struct file *filp, /* include/linux/fs.h */
char *buffer, /* buffer */
size_t length, /* buffer length */
loff_t * offset )
{
int byte_read = 0;
int rx;
int rx1;
char vr_data1;
while (length)
{
if (inputBuffer.busy_buff == 0)
{
rx = inputBuffer.dataB[byte_read];
}
else
{
rx = inputBuffer.dataA[byte_read];
}
if (length > 0)
{
vr_data1 = rx;
put_user( vr_data1, buffer++ );
length--;
}
if (length > 0)
{
rx1 = rx >> 8;
vr_data1 = rx1;
put_user( vr_data1, buffer++ );
length--;
}
if (length > 0)
{
rx1 = rx >> 16;
vr_data1 = rx1;
put_user( vr_data1, buffer++ );
length--;
}
if (length > 0)
{
rx1 = rx >> 24;
vr_data1 = rx1;
put_user( vr_data1, buffer++ );
length--;
}
byte_read++;
if (byte_read > INPUT_BUFFER_SIZE) break;
}
return (4*byte_read);
}
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
Цитата Но при увеличении длины посылки более 32 бит, отправляется только 32 бита. Работает ли этот режим для посылок длиннее 32 бит?
в сообщение за январь приведена ссылка на форум, откуда растет еще одна ссылка, где ребята с осциллографом показали Silicon bug espi imx53 как раз для вариантов 32+ бита..
что показывает осциллограф в вашем случае?
На любой вопрос есть любой ответ.
buletz
Пункты: 5920
Регистрация: 16.11.2011
Пол: Мужчина
В мануале на imx53 есть такие строки:
ECSPIx_CONREG field descriptions
....
31–20 BURST LENGTH In slave mode, only when SS_CTL is cleared, this field will take effect in the transfer.
При этом для регистра ECSPIx_CONFIGREG, в котором содержится SS_CTL указано следующее:
11–8 SS CTL ....1 In slave mode - an SPI burst is completed by the Chip Select (SS) signal edges. (SSPOL = 0: rising edge; SSPOL = 1: falling edge) The RXFIFO is advanced whenever a Chip Select (SS) signal edge is detected or the shift register contains 32-bits of valid data.
Может быть попробовать SS_CTL выставить в ноль?
Maxim
Пункты: 722
Регистрация: 14.12.2013
imx53 - master, ads1274 - slave
CONREG = 0x00F100F8 - на осциллографе 16 бит
CONREG = 0x01F100F8 - на осциллографе 32 бита
CONREG = 0x05F100F8 - на осциллографе только 32 бита
SS_CTL are ignored if the SMC bit is set. Пробовал SMC = 0, SS_CTL = 0 и 1. В обоих случаях передается только 32 бита.
Какие есть варианты решения проблемы с пропусками прерываний? Есть ли настройки в ядре или проблему можно решить только с помощью железа (складывать данные в буфер с помощью железки, а затем передавать их в linux)?