Как структурировать встроенное приложение при разделении кода приложения и драйвера для периферийного устройства (UART)? - PullRequest
1 голос
/ 17 апреля 2020

Итак, я пишу драйверы для UART на STM32, и хотя у меня вроде есть идея по разметке структуры, я все же хотел бы уточнить до ее реализации, а также рассмотреть вопрос о том, насколько важно сохранить код. чистый и организованный.

Итак, у меня есть main.c, sensor.c (файл приложения, использующий слой UART), hal_usart.c (это файл драйвера).

Я слышал разные о том, как код приложения не должен иметь никакого представления об API драйвера, и прочитал на днях статью о том, что вы можете отделить код датчика от кода драйвера HAL, используя указатели функций, но не знаете, как я могу это сделать здесь, и если это будет отделять его, учитывая, что я все еще передаю ссылку на структуру USART_Handle, которая содержит такую ​​информацию, как baudRate, parityControl, wordLength, ссылку на USART_TypeDef et c.

Ниже приведен фрагмент моей идеи:

// main.c
static USART_Handle pUSART;

int main(void) {
   // initialize clocks/HAL 

   // ...initialize USART struct

   // Get data via UART (calling application API)
   GetData(&pUSART);   
}

// sensor.c (application)
void GetData(USART_Handle *pUSART) {
     HAL_USART_TX(); 
     HAL_USART_RX();   // assuming data is stored in one of the struct members
}

// hal_usart.c  (Driver file)
void HAL_USART_TX() {} 
void HAL_USART_RX() {}



Ответы [ 2 ]

1 голос
/ 20 апреля 2020

Утилита указателей на функции является максимальной, если вы хотите изменить указатель во время работы программы; или, другими словами, когда у вас есть как минимум две разные функции и, основываясь на каком-то условии, вы хотите использовать одну или другую.

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

Если вам не нужно что-то менять во время выполнения, вы можете просто написать свою подпрограмму датчика, сделав ее вызвать функцию чтения (внешнюю), определенную в каком-то другом файле. В документации по сенсорной подпрограмме просто должно быть указано, что где-то должна быть определена подпрограмма «int sensor_get_data ()».

Это включает в себя разработку собственного «внутреннего протокола» на основе того, какие данные поступают и поступают из «отстраненные водители». Например, подпрограмма датчика, которая работает с точной моделью датчика, может потребовать отправки команды и получения ответа. Вы можете написать свою подпрограмму датчика, которая создает команду и декодирует ответ, и убирает подробности низкого уровня, оборачивая их все в одну функцию "int sensor_get_data (int command)". Затем вы связываете или включаете код датчика и внедряете функцию sensor_get_data () в main.

Функция main () не знает подробности датчика, она знает только, что код датчика, когда это необходимо, вызовет функцию sensor_get_data (). Но эта функция может использовать UART, или i2 c, или spi, даже если датчик не заметил этого; более того, эта подпрограмма может использовать любой из трех портов, возможно, на основе параметров, которые пользователь может изменить во время выполнения.

Все это можно назвать «механизмом обратного вызова» и реализует своего рода разделение между объявлением и реализацией, как уже упоминалось в другом ответе. То, что я изобразил, не отличается от этого, но это stati c - программа компилируется с фиксированным обратным вызовом вместо передачи указателя на функцию при каждом вызове.

1 голос
/ 20 апреля 2020

Поскольку вы хотите разделить код приложения и код драйвера во встроенной системе, я предлагаю вам изучить, как реализовать функции обратного вызова в C, также встроенных системах.

Вот ссылка .

...