Перейти к содержанию
    

Помогите с указателями на функции Си

всем доброго времени суток.

с проектом непонятки следующие:

1. Есть АЦП. В него отправляют байты по SPI и также получают. В соответствующем для АЦП h-файле есть записи

typedef void (* tADC_TxByte)(unsigned char Data);
typedef unsigned char (* tADC_RxByte)(void);

typedef struct
{
    tADC_TxByte TxByte;
    tADC_RxByte RxByte;
} tADC_Device;

extern tADС_Device ADCDevice;

 

2. Есть для микроконтроллера файлы SPI.c и SPI.h где определены соответствующие функции для работы с SPI

void SPI_TxByte(unsigned char Data);

unsigned char SPI_RxByte(void);

 

Так вопрос вот в чем - как правильно привязать обращения типа 

data[0] = ADCDevice.RxByte() к SPI_RxByte(void);

и соответственно 

ADCDevice.TxByte(0xA0) к SPI_TxByte(0xA0);

???

Есть ли способ увязать не корректируя файлы "ADC.c" и "ADC.h"? Например создать файлик "ext_SPI.h" и правильно его инклудить. 

Прошу ногами сильно не пинать. Наверное это предновогодний ступор.

Изменено пользователем Halfback

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

35 минут назад, Halfback сказал:

Так вопрос вот в чем - как правильно привязать обращения типа 

data[0] = ADCDevice.RxByte() к SPI_RxByte(void);

и соответственно 

ADCDevice.TxByte(0xA0) к SPI_TxByte(0xA0);

Что такое "привязать"? непонятно...

Вы не знаете как записать указатель на функцию  SPI_RxByte() в соответствующий член структуры ADCDevice?  :shok:

 

ЗЫ: И зачем интересно что-то записывать в АЦП?

Да и разрядность какая-то странная... Неужто у вас АЦП 8-разрядный???

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 25.12.2023 в 13:39, jcxz сказал:

ЗЫ: И зачем интересно что-то записывать в АЦП?

в АЦП есть регистры для конфигурации усиления входа, смещений и т.п. На счет разрядности обращения к АЦП - к сути вопроса это отношения не имеет.

В 25.12.2023 в 13:39, jcxz сказал:

Что такое "привязать"? непонятно...

при обращении ADCDevice.TxByte(0xA0) реально должна вызываться SPI_TxByte(0xA0)

в "ADC.h" объявлен тип tADC_TxByte как указатель на функцию, принимающая в качестве аргумента unsigned char и ничего не возвращающая.  Но вот меня клинит на том, где правильно написать что tADC_TxByte SPI_RxByte;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ADCDevice.TxByte = SPI_TxByte;

С приемом аналогично.

17 минут назад, Halfback сказал:

Но вот меня клинит на том, где правильно написать что tADC_TxByte SPI_RxByte;

Обычно в примерах из интернета это делают в функции ADCInit(), которую вызывают в начале main().

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

АЦП - внешний, сидит на интерфейсе SPI? Окей, тогда интерфейс SPI должен иметь функции отправки и приема байт:

void SPI_Send(uint8_t byte);
uint8_t SPI_Recieve();

И если с SPI_Send() всё понятно - она вызывается для отправки очередного байта, то ф-ция SPI_Recieve() должна вызываться при готовности принятого байта, например, в прерывании. Причем, поскольку ф-ция, реализующая прерывание, не принимает и не передает параметров, то целесообразно сделать функцию-коллбэк SPI_Receive_Callback(), которая будет вызываться из прерывания SPI, а внутри этого коллбэка будет вызываться ф-ция SPI_Recieve() и складывать очередной принятый байт в массив static uint8_t spi_rx, объявленную вне коллбэка, чтобы к этому массиву могла получить доступ другая функция-читатель принятых байт.

Другой вариант - сделать прием SPI по DMA. И тогда нужно будет лишь задать число принимаемых байт, DMA автоматически примет их и сложит в указанное место. Останется только прочитать массив по сигналу DMA Transfer Complete.

55 минут назад, Halfback сказал:

при обращении ADCDevice.TxByte(0xA0) реально должна вызываться SPI_TxByte(0xA0)

А почему бы сразу и не записать вызов именно конечной ф-ции, поскольку никакой модификации по пути не происходит?

Однако, как правило, у внешних микросхем есть некоторый порядок действий. Поэтому, между ф-циями АЦП и ф-циями SPI есть некоторая прослойка, выполняющая этот самый порядок действий. В частности, например может выглядеть вот так:

voie ADC_Init(void)
{
  CS_LO();
  SPI_Send(POW_ON);
  SPI_Send(SET_GAIN);
  SPI_Send(gain_value);
  CS_HI();
}

void ADC_Start(int channel)
{
  CS_LO();
  SPI_Send(SELECT_CHANNEL);
  SPI_Send(channel);
  SPI_Send(START_MEAS);
  CS_HI();
}

void ADC_Receive(void)
{
  CS_LO();
  uint8_t rx = SPI_Receive();

 

1 час назад, jcxz сказал:

И зачем интересно что-то записывать в АЦП?

Я так понял, это внешний АЦП, и у него есть некоторые конфигурационные и командные регистры 🙂 

Изменено пользователем EdgeAligned

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 минуты назад, EdgeAligned сказал:

И если с SPI_Send() всё понятно - она вызывается для отправки очередного байта, то ф-ция SPI_Recieve() должна вызываться при готовности принятого байта, например, в прерывании.

Вы забыли написать, что в SPI передача и приём идут одновременно. Поэтому, если автор хочет блокирующий режим работы с SPI (без ПДП и прерываний), то функция у него должна быть одна - принимающая и возвращающая значение одного и того же размера.

22 минуты назад, EdgeAligned сказал:

Я так понял, это внешний АЦП, и у него есть некоторые конфигурационные и командные регистры 🙂

Тогда - судя по прототипу функции передачи, там всего один регистр. 8 битный.

Но скорее всего, вангую - этой функцией автор инициирует приём байтов из АЦП, а не запись неких регистров. Так себе идея конечно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем всем спасибо за потраченное время. А ларчик то открывался просто. 

Ведь было же в "ADC.h" объявлено 

extern tADС_Device ADCDevice;

это значит что в main.c можно написать так

tAD779X_Device ADCDevice = {
        .TxByte = SPI_TxByte,
        .RxByte = SPI_RxByte
        };

и причем тут потуги на тему как надо работать с АЦП, что необходимость DMA (хотя кто сказал что это какой-нибудь STM32 а не AVR/PIC) и т.д. и т.п.???  совершенно непонятно зачем некоторые товарищи пытались уводить тело вопроса в сторону.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...уводить в сторону более правильного решения. Но если "некоторые авторы" предпочитают идти по лесному болоту вместо езды по хайвею, то пожалста, это их путь, никто не в праве противостоять иму.

Кстати, по-моему, никто из нас тут не произнес магического заклинания "STM32" 🙂 Ну а DMA есть же не только в STM32 ведь! Его нет, пожалуй, только в самых немощных и старых МК.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 25.12.2023 в 15:28, EdgeAligned сказал:

Но если "некоторые авторы" предпочитают идти по лесному болоту вместо езды по хайвею

Вы даже не удосужились понять суть моего вопроса и начали давать "ценные" советы как по SPI работать с АЦП. А если бы это был не АЦП а другое устройство? И не SPI ?  Суть вопроса данного топика от этого не поменялась бы никак. Надеюсь понятно пояснил.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 25.12.2023 в 15:22, Halfback сказал:

это значит что в main.c можно написать так

tAD779X_Device ADCDevice = {
        .TxByte = SPI_TxByte,
        .RxByte = SPI_RxByte
        };

Я бы ещё const добавил. Зачем это все держать в ОЗУ.

#

Несколько лет назад писал драйвер для этого АЦП. Если память не подводит, сделал в ОЗУ структуру регистров и читал/писал во все сразу за один проход. Получается намного быстрее и проще, чем к каждому регистру по отдельности. 

Например, выловил в статусе флаг Ready, а в дата регистре уже готовые данные лежат. Не надо два раза бегать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 25.12.2023 в 15:37, MrYuran сказал:

Я бы ещё const добавил. Зачем это все держать в ОЗУ

Спасибо, совет хороший, но в моем случае не применим т.к. в структуре ADCDevice будут поля которые могут меняться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 минут назад, Halfback сказал:

А если бы это был не АЦП а другое устройство?

"А если бы он вёз патроны?" (С) 🙂 Да какая разница, АЦП, ЦАП, дисплей по параллельному интерфейсу или I2C. Всё одинаково.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подкину на вентилятор для местных гуру программирования

ещё есть магическое слово __weak

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 hours ago, Halfback said:
typedef void (* tADC_TxByte)(unsigned char Data);
typedef unsigned char (* tADC_RxByte)(void);

typedef struct
{
    tADC_TxByte TxByte;
    tADC_RxByte RxByte;
} tADC_Device;

extern tADС_Device ADCDevice;

Может, проще перейти на С++ и не утруждать руки и мозг нажиманием кнопок там, где это же самое может хорошо сделать компилятор?

class ADC_device
{
	public:
		ADC_device( void );
		virtual ~ADC_device( void );

		virtual uint8 txByte( uint8 a_byte );
		virtual bool  rxByte( uint8* a_byte );
};

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...