Jump to content
    

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

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

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

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" и правильно его инклудить. 

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

Edited by Halfback

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

 

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

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

Share this post


Link to post
Share on other sites

В 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;

Share this post


Link to post
Share on other sites

ADCDevice.TxByte = SPI_TxByte;

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

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

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

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

Share this post


Link to post
Share on other sites

АЦП - внешний, сидит на интерфейсе 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 сказал:

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

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

Edited by EdgeAligned

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

extern tADС_Device ADCDevice;

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

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

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

#

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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 );
};

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...