Halfback 0 25 декабря, 2023 Опубликовано 25 декабря, 2023 (изменено) · Жалоба всем доброго времени суток. с проектом непонятки следующие: 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" и правильно его инклудить. Прошу ногами сильно не пинать. Наверное это предновогодний ступор. Изменено 25 декабря, 2023 пользователем Halfback Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 239 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба 35 минут назад, Halfback сказал: Так вопрос вот в чем - как правильно привязать обращения типа data[0] = ADCDevice.RxByte() к SPI_RxByte(void); и соответственно ADCDevice.TxByte(0xA0) к SPI_TxByte(0xA0); Что такое "привязать"? непонятно... Вы не знаете как записать указатель на функцию SPI_RxByte() в соответствующий член структуры ADCDevice? ЗЫ: И зачем интересно что-то записывать в АЦП? Да и разрядность какая-то странная... Неужто у вас АЦП 8-разрядный??? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Halfback 0 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба В 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; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 140 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба ADCDevice.TxByte = SPI_TxByte; С приемом аналогично. 17 минут назад, Halfback сказал: Но вот меня клинит на том, где правильно написать что tADC_TxByte = SPI_RxByte; Обычно в примерах из интернета это делают в функции ADCInit(), которую вызывают в начале main(). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 81 25 декабря, 2023 Опубликовано 25 декабря, 2023 (изменено) · Жалоба АЦП - внешний, сидит на интерфейсе 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 сказал: И зачем интересно что-то записывать в АЦП? Я так понял, это внешний АЦП, и у него есть некоторые конфигурационные и командные регистры 🙂 Изменено 25 декабря, 2023 пользователем EdgeAligned Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 239 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба 22 минуты назад, EdgeAligned сказал: И если с SPI_Send() всё понятно - она вызывается для отправки очередного байта, то ф-ция SPI_Recieve() должна вызываться при готовности принятого байта, например, в прерывании. Вы забыли написать, что в SPI передача и приём идут одновременно. Поэтому, если автор хочет блокирующий режим работы с SPI (без ПДП и прерываний), то функция у него должна быть одна - принимающая и возвращающая значение одного и того же размера. 22 минуты назад, EdgeAligned сказал: Я так понял, это внешний АЦП, и у него есть некоторые конфигурационные и командные регистры 🙂 Тогда - судя по прототипу функции передачи, там всего один регистр. 8 битный. Но скорее всего, вангую - этой функцией автор инициирует приём байтов из АЦП, а не запись неких регистров. Так себе идея конечно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Halfback 0 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба В общем всем спасибо за потраченное время. А ларчик то открывался просто. Ведь было же в "ADC.h" объявлено extern tADС_Device ADCDevice; это значит что в main.c можно написать так tAD779X_Device ADCDevice = { .TxByte = SPI_TxByte, .RxByte = SPI_RxByte }; и причем тут потуги на тему как надо работать с АЦП, что необходимость DMA (хотя кто сказал что это какой-нибудь STM32 а не AVR/PIC) и т.д. и т.п.??? совершенно непонятно зачем некоторые товарищи пытались уводить тело вопроса в сторону. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 81 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба ...уводить в сторону более правильного решения. Но если "некоторые авторы" предпочитают идти по лесному болоту вместо езды по хайвею, то пожалста, это их путь, никто не в праве противостоять иму. Кстати, по-моему, никто из нас тут не произнес магического заклинания "STM32" 🙂 Ну а DMA есть же не только в STM32 ведь! Его нет, пожалуй, только в самых немощных и старых МК. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Halfback 0 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба В 25.12.2023 в 15:28, EdgeAligned сказал: Но если "некоторые авторы" предпочитают идти по лесному болоту вместо езды по хайвею Вы даже не удосужились понять суть моего вопроса и начали давать "ценные" советы как по SPI работать с АЦП. А если бы это был не АЦП а другое устройство? И не SPI ? Суть вопроса данного топика от этого не поменялась бы никак. Надеюсь понятно пояснил. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 27 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба В 25.12.2023 в 15:22, Halfback сказал: это значит что в main.c можно написать так tAD779X_Device ADCDevice = { .TxByte = SPI_TxByte, .RxByte = SPI_RxByte }; Я бы ещё const добавил. Зачем это все держать в ОЗУ. # Несколько лет назад писал драйвер для этого АЦП. Если память не подводит, сделал в ОЗУ структуру регистров и читал/писал во все сразу за один проход. Получается намного быстрее и проще, чем к каждому регистру по отдельности. Например, выловил в статусе флаг Ready, а в дата регистре уже готовые данные лежат. Не надо два раза бегать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Halfback 0 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба В 25.12.2023 в 15:37, MrYuran сказал: Я бы ещё const добавил. Зачем это все держать в ОЗУ Спасибо, совет хороший, но в моем случае не применим т.к. в структуре ADCDevice будут поля которые могут меняться. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 81 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба 11 минут назад, Halfback сказал: А если бы это был не АЦП а другое устройство? "А если бы он вёз патроны?" (С) 🙂 Да какая разница, АЦП, ЦАП, дисплей по параллельному интерфейсу или I2C. Всё одинаково. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 59 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба Подкину на вентилятор для местных гуру программирования ещё есть магическое слово __weak Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 25 декабря, 2023 Опубликовано 25 декабря, 2023 · Жалоба 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 ); }; 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться