Сначала, как всегда, мелкие придирки (поскольку чужой труд ругать куда легче, чем самому что-то полезное сделать).
DruidCat писал(а):Код: Выделить всё
//********************************************************************************
// Файл : send_usart.h
...
// Назначение : Выводим строку и число по USART через универсальную функцию (1)
...
#include <avr/io.h> (4)
#include <string.h> //Вызов функции strlen (2)
//Настройка регистра UCSRB (3)
#define UCSRB_RXCIE 0 //Если 1, то разрешение прерывания по завершению приема по uSART
#define UCSRB_TXCIE 0 //Если 1, то разрешение прерывания по завершении передачи по uSART
#define UCSRB_UDRIE 0 //Если 1, то разрешение прерывания при очистке регистра данных USART
#define UCSRB_RXEN 0 //Если 1, то разрешение приема по USART
#define UCSRB_TXEN 1 //Если 1, то разрешение передачи по USART
//Настройка скорости передачи данных по USART
#define F_CPU 8000000UL //Частота МК
#define USART_SPEED 9600 //Скорость USART желаемая
#define BAUD ((F_CPU/(USART_SPEED*16UL)) - 1) //Формула расчета БОД
...
(1) Прекрасная привычка - приводить в начале модуля комментарии, полностью разъясняющие его назначение. Не нужно проводить детективное расследование, чтобы понять, что он делает. (Если еще и отформатировать содержательный комментарий в стиле
Doxygen, то ему вообще цены не будет).
Плохая привычка - обманывать в этих комментариях доверчивого читателя. На самом деле в нем
нет средств для вывода чисел, только строк. Значит, только о выводе строк и упоминаем. Как Мефистофель справедливо корил Фауста в аналогичном случае, "Ты обещанье дал напрасно".
(2) Лучше не засорять заголовочный файл вложенными заголовками, которые не используются в нем непосредственно. В данном случае таким явно лишним заголовком является
<string.h>. Его можно вполне безболезненно убрать. Кому понадобятся функции работы со строками, пусть включает их сам.
(3) Опять же, модулю-клиенту драйвера, который выводит через него данные вовсе не обязательно знать о константах настройки USART вроде тактовой частоты или флага разрешения прерывания по приему. Это тот самый случай, о котором в книге Экклезиаста сказано: "и кто умножает познания, умножает скорбь" (и заодно размер заголовочного файла). Все эти определения вполне уместно вынести непосредственно в тот файл, где они будут использоваться, поскольку никому более они не интересны. Более того, есть соблазн, например, "включить" прерывания от передатчика, переопределив
UCSRB_UDRIE, но ни к чему хорошему это не приведет, поскольку прерывания не поддержаны реализацией модуля (хотя из заголовка это никак не видно). А коли так, лучше прятать эти детали поглубже на тот уровень, где они действительно используются.
(4) Заголовок
<avr/io.h> тоже не нужен клиентам нашего драйвера, поскольку мы договорились все аппаратно-заависимые детали упрятать в реализацию драйвера.
Теперь в заголовочном файле остается лишь прототип функции
Код: Выделить всё
void send_usart (char String[]); //Функция вывода символов.
Этого вполне достаточно для клиентов, которые желают выводить строки. Остальное для них явно лишнее.
DruidCat писал(а):Код: Выделить всё
// Файл : send_usart.c
...
void send_usart (char String[]) //Функция вывода символов
{
int N = strlen (String); //Подсчитываем колличество символов в массиве
for (int i = 0; i != N; i++) //Цикл вывода строки по USART
...
Делаем явно лишнюю работу: сначала заставляем функцию
strlen перебрать в цикле все литеры в строке, пока не наткнется на завершающий '\0', и подсчитать их количество; затем опять перебираем те же литеры для вывода в USART. Проще было бы выводить их, пока не встретим '\0', а количество литер - опять же "познания, умножающие скорбь":
В принципе мелочь - одна лишняя переменная и десяток-другой микросекунд на лишний вызов библиотечной функции; но, с другой стороны, чем чище код - тем выше его качество.
DruidCat писал(а):Код: Выделить всё
while ((UCSRA & _BV(UDRE)) == 0); //Пауза, цикл работает пока регистр данных не опустошён
1. Вместо
(условие == 0) с таким же успехом можно писать
(!условие). Второй вариант читабельнее, поскольку конфузливо скрывает факт, что C так и не обзавелся полноценным булевским типом.
2. Вместе с макросом
_BV в библиотеке определены также еще несколько полезных макросов для работы с битами. Один из них:
Код: Выделить всё
#define loop_until_bit_is_set( sfr, bit ) do { } while (bit_is_clear(sfr, bit))
ожидает в пустом цикле, пока в регистре ввода-вывода
sfr установится бит
bit. Грех не воспользоваться готовеньким, тем более что библиотечные элементы, как правило, хорошо оптимизированы и протестированы.
Теперь по поводу:
DruidCat писал(а):Код: Выделить всё
//Вызывать из основного модуля эту функцию не нужно.
__attribute__((naked, section(".init7")))
void init_usart (void)// Функция настройки параметров USART.
Программирование - это постоянный выбор компромисса между несколькими правильно работающими вариантами: быстродействие против компактности кода, эффективность против скорости разработки и т.д. Крайне редко попадаются идеальные варианты, гораздо чаще - более предпочтительные в данных конкретных условиях. В решении вынести инициализацию в секцию
.init7 также есть свои плюсы и минусы.
Плюсы: неявный вызов функции инициализации гарантирует, что клиент не забудет инициализировать USART и не попытается слать в него данные без инициализации.
Минусы: если Вы сохраните взятый высокий темп обучения, то довольно скоро освоите C (он на самом деле весьма прост) и перейдете на новый уровень - разработку
качественного программного обеспечения. Например, если вооружитесь технологией разработки ПО через тестирование (
TDD), то вместо
DruidCat писал(а):Проверял работу на симуляторе. Вроде работает.

начнете говорить: "Я завершил тестирование программы, все тесты прошли успешно". Будете стремиться к максимальному покрытию кода тестами, в том числе и кода инициализации. Для полноценного тестирования нетривиального кода инициализации, скорее всего, потребуются мок-объекты. В случае неявной инициализации возможны проблемы с тестированием этой части кода. По крайней мере, я не очень представляю себе, как это сделать с моими предпочтительными инструментами модульного тестирования кода C
Unity и
CMock.
Придется делать выбор, в данном случае - между профилактикой склероза у клиентов и полнотой покрытия кода тестами.
Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет.
J. Ganssle