Вы говорите о конкретном HAL в Windows или Linux или о чем-то или вообще?
Например, при доступе к регистрам из драйвера устройства (код, который управляет устройством, не обязательно должен быть ядром или вообще иметь операционную систему), я обычно рекомендую создавать такие функции, как PUT32 (адрес, данные), data = GET32 (адрес). Или пишите и читайте, как хотите. Дело в том, чтобы избежать создания указателя с адресом и непосредственного использования этого указателя. В решении с типом указателя повышается производительность, а производительность снижается до абстрактного PUT32 (). Однако я использую его потому, что если код достаточно чистый, чтобы его можно было использовать как часть драйвера ядра для этой ОС, драйвер ядра для этой ОС, запустить автономно встроенный, подключиться к hdl-моделированию логики, запустить на процессор на той же микросхеме или запущенный на главном компьютере, который проникает в микросхему через PCI или jtag и т. д. Один кусок кода повторно используется с момента рождения логики (hdl sim) до драйвера ядра конечного пользователя.
Возможно, больше к вашему вопросу, хотя подумайте о UART, вы хотите отправить несколько байтов и получить несколько байтов правильно? Создайте функцию uart_send () и функцию uart_recv (), все, что находится выше уровня абстракции, использует эти две функции. Когда вы нацеливаете этот код на конкретную платформу, вы реализуете эти функции для конкретного uart на этом конкретном оборудовании. позже вы можете заменить этот UART чем-то другим, при условии, что новый UART может отправлять и получать код над уровнем абстракции, менять его не нужно. Несмотря на то, что вы создали слой абстракции с указанными выше функциями, я лично все равно использовал бы функции PUT8 () и GET8 () в реализации uart_send () и uart_recv () для конкретного uart, а в отдельном файле - PUT8 ( ) и GET8 ().
Сколько уровней абстракции между драйвером и фактическим оборудованием, как и где часто зависит от задачи и оборудования.