Сообщение от miwutka
|
вот весь...
|
Даа, конечно, когда в такую кучу свален весь проект от начала до конца - немудрено, что заглючит на ровном месте.
Малость опережу dosikus-а, выскажу свое мнение.
Очень желательно разделять проект по файлам на модули. С точки зрения конечного кода почти что все равно, как делать, а вот с точки зрения удобства написания-редактирования, тут уж другое дело. Ведь даже просто пролистывать 20-страничную единую портянку весьма муторно, не говоря уже о редактировании.
Поэтому, практически все должно быть оформлено в виде отдельных модулей.
Конечно, надо вначале разобраться и научиться делать такое разделение, ибо есть заморочки с тем, что куда подключить, где объявить и как связать передаваемые значения функций.
В языке Си это называется областью видимости функций, переменных. Надо, по возможности, избегать глобального объявления переменных, когда они видны (доступны) на протяжении всего проекта.
Переменные, функции, дефайны, используемые только внутри одного модуля, и должны быть доступны только в этом модуле и не выходить за его пределы. Связь между модулями - через параметры функций.
Вот например работу с неким дисплеем можно представить так:
-- самый нижний уровень. Файл драйвера интерфейса дисплея (I2C, SPI, параллельный), в котором будут функции низкоуровневой работы с физическим интерфейсом - его первоначальная настройка в функции Interface_Initial(), функции отправки/приема одного байта или массива байтов через интерфейс, безотносительно куда, не конкретно дисплею, а просто куда-то.
Ну и если необходимо требованиями самого дисплея, отправка байта сопровождается передергиванием еще каких-то ног, как например в ILI93xx, это тоже должно быть включено в функции отправки/приема.
Если используется DMA, то производим его предварительную настройку и используем DMA в отправке/приеме.
Доступ к функциям файла драйвера интерфейса осуществляется через его заголовочный файл, в котором объявлены эти функции. Однако, внутренние функции и переменные, используемые только внутри модуля, должны объявляться в самом файле .c, чтобы они не мешались в других файлах.
Заголовочный файл драйвера (name_file.h) подключается (#include name_file.h) как к самому файлу драйвера name_file.c, так и к другому файлу, из которого будут вызываться функции этого драйвера.
-- файл драйвера дисплея, в котором прописаны функции непосредственно отправки/приема команд дисплея, безотносительно его физического интерфейса, то есть, функция отправки команды дисплея не знает, через какой интерфейс - SPI, I2C работает дисплей. Функции этого файла обеспечивают взаимодействие с дисплеем через какой-либо физический интерфейс, описанный функциями драйвера этого интерфейса, отправляют команды и данные в дисплей, принимают ответы дисплея. Именно в этот файл помещаются такие функции, как включение и начальная настройка параметров дисплея, установка произвольной области вывода на дисплей, изменение настроек дисплея, вывод произвольной информации на дисплей.
Все данные передаются исключительно через параметры функций - либо непосредственно, либо через ссылки (адреса переменных).
Так же есть заголовочный файл с объявлениями функций, вызываемых из других файлов, этот файл точно так же будет подключаться к другим файлам, из которых будет вестись управление дисплеем.
Вот, эти 4 файла будут драйвером конкретного дисплея. (сюда же можно присобачить еще и драйвер управления подсветкой дисплея).
Что это дает. Допустим, есть дисплей с несколькими способами подключения, и мы захотели подключить не по I2C, а по SPI - меняем (переписываем) только драйвер физического интерфейса. функции записи/чтения дисплея работают как и прежде. Или пришлось заменить модель дисплея с другим набором команд - меняем драйвер дисплея, не затрагивая построения изображения.
Работать с дисплеем нужно через функции, предоставляемые драйвером дисплея верхнего уровня. Опять же, эта работа должна (желательно) вестись из отдельного файла (модуля) компоновки кадра изображения. То есть, не надо пихать в main.c функцию вывода буквы шрифта.
В майне может остаться вызов функции Welcome_Screen(), а эта самая функция модуля подготовки кадра должна закрасить экран синим цветом и вывести белый текст "Здрасьте!". К маин-файлу подключается (#include) заголовочный файл компоновщика кадра, в котором определена функция Welcome_Screen, а значит, она доступна для вызова из того файла, к которому подключен этот заголовочный файл. Все остальные внутренние функции и переменные, не определенные в заголовочном файле, не будут доступны из маин-файла, но будут вызываться в соответствии с их иерархией подключений, вплоть до самой нижней функции отправки байта в дисплей по интерфейсу.
Таким образом, функцию Welcome_Screen(), записываемую всего одной строчкой, можно очень легко поставить в любое место маин-файла, передвигая ее между такими же функциями других модулей. А значит, легче и ориентироваться в собственной проге, легче отыскивать косяки.
Да и само написание проги становится модульным.
Абстрактный пример - во вложении.
На первом этапе можно создать просто набросок, без написания осмысленного контента функций, а для того, чтобы проверить взаимодействие между модулями, подготовить скелет.
Причем, даже практически пустые функции-заглушки, в которых прописано только базовое взаимодействие с другими функциями, будут верно работать между собой в целом. Вначале можно даже не прибегать к настройке аппаратной части МК, а строить только взаимосвязи.
Например, если нет физически дисплея или не написан драйвер интерфейса, то ответ дисплея можно эмулировать в функции, просто прописав руками нужные байты ответа.
Можно даже ваапще подключить файл-эмулятор дисплея и спокойно писать прогу без физического дисплея. Визуальные косяки конечно видны не будут, но в остальном - прокатит.
А уже потом, по ходу дела, прорабатываем конкретное наполнение функций, с привязкой к "железу".
Конечно, все описанное можно запросто написать и в одном файле по тем же законам. Однако, портянка текста получится заачоодная. Так что лучче уж сразу научиться разбивать на файлы.
Ну и еще, в процессе написания совершенно не надо стесняться комментировать. Комменты - они ведь для самого себя, они самому себе показывают степень завершенности/незавершенности, какие-то свои мысли и пометки, для самого же себя. Вот например прервался на пару дней, потом открываешь и видишь - на чем остановился, чего еще делать осталось. Конечно, средства разработки имеют свои инструменты наблюдения за процессом типа планировщика задач.