Учебный курс. Подключение LCD к микроконтроллеру. Разбиваем программу на модули
Зачем разбивать программу на модули/файлы
Возможность повторного использования кода
Возможность раздельной компиляции
Возможность совместной работы над проектом
Область видимости переменных/функций, находящихся в разных модулях
extern unsigned char key;
extern unsigned char key = 0; //неправильно. Вызовет ошибку!
static unsigned char LCD_CheckBF(void)
{
//тело функции
}
Попытка использовать эту функцию в другом файле вызовет ошибку компиляции.
Как разбить программу на модули  
Открываем наш старый проект.
Создаем в IARe два файла – lcd_lib.h и lcd_lib.c.
Сохраняем файлы в папке проекта.
Добавляем файл lcd_lib.c в наш проект. (Правой кнопкой мышки кликаем в workspace`e и в открывшемся меню выбираем Add > Add files…).
В lcd_lib.c с помощью директивы include подключаем заголовочный файл -
#include “lcd_lib.h”.
В lcd_lib.h добавляем строчки
#ifndef LCD_LIB_H
#define LCD_LIB_H
#endif
Теперь у нас есть своего рода заготовка и нам нужно ее наполнить содержимым.
Содержимое заголовочного файла
Подключаемые библиотеки
#include <ioavr.h>
#include <intrinsics.h>
Макроопределения портов
//порт к которому подключена шина данных ЖКД
#define PORT_DATA PORTD
#define PIN_DATA  PIND
#define DDRX_DATA DDRD
//порт к которому подключены управляющие выводы ЖКД
#define PORT_SIG PORTB
#define PIN_SIG  PINB
#define DDRX_SIG DDRB
//Номера выводов к которым подключены управляющие выводы ЖКД 
#define RS 5
#define RW 6
#define EN 7
Макроопределение тактовой частоты микроконтроллера
#define F_CPU 8000000
Прототипы функций
void LCD_Init(void);  //инициализация портов и жкд
void LCD_WriteData(unsigned char data);   //выводит байт данных на жкд
void LCD_WriteCom(unsigned char data);   //посылает команду жкд
  Прототип функции – это объявление, содержащее тип возвращаемого значения, название функции и ее параметры.   
Содержимое файла lcd_lib.c
Макросы для работы с битами и макросы для программных задержек
#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)           reg |= (1<<(bit))    
#define _delay_us(us)      __delay_cycles((F_CPU / 1000000) * (us));
#define _delay_ms(ms)     __delay_cycles((F_CPU / 1000) * (ms));
Определение всех функций
//функция записи команды 
void LCD_WriteCom(unsigned char data)
{
  ClearBit(PORT_SIG, RS);           //установка RS в 0 - команды
  PORT_DATA = data;               //вывод данных на шину индикатора 
  SetBit(PORT_SIG, EN);            //установка E в 1
  _delay_us(2);
  ClearBit(PORT_SIG, EN);           //установка E в 0 - записывающий фронт
  _delay_us(40);
}
//функция записи данных
void LCD_WriteData(unsigned char data)
{
  SetBit(PORT_SIG, RS);            //установка RS в 1 - данные
  PORT_DATA = data;               //вывод данных на шину индикатора 
  SetBit(PORT_SIG, EN);            //установка E в 1
  _delay_us(2);
  ClearBit(PORT_SIG, EN);          //установка E в 0 - записывающий фронт
  _delay_us(40);
}
//функция инициализации
void LCD_Init(void)
{
  DDRX_DATA = 0xff;
  PORT_DATA = 0xff;    
  DDRX_SIG = 0xff;
  PORT_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
  ClearBit(PORT_SIG, RW);
  _delay_ms(40);
  LCD_WriteCom(0x38); //0b00111000 - 8 разрядная шина, 2 строки
  LCD_WriteCom(0xf);  //0b00001111 - дисплей, курсор, мерцание включены
  LCD_WriteCom(0x1);  //0b00000001 - очистка дисплея
  _delay_ms(2);
  LCD_WriteCom(0x6);  //0b00000110 - курсор движется вправо, сдвига нет
}
Подключаем библиотеку к проекту
В принципе все готово. 
Сохраняем оба файла. 
Подключаем нашу библиотеку к файлу main.c.
 
#include "lcd_lib.h"
Убираем из него все лишнее. И добавляем вывод слова “Test.”
Вот что должно получиться.
#include <ioavr.h>
#include "lcd_lib.h"
int main( void )
{
  LCD_Init();
  LCD_WriteData('T');
  LCD_WriteData('e');
  LCD_WriteData('s');
  LCD_WriteData('t');
  LCD_WriteData('2');
  while(1);
  return 0;
}
Компилируем проект… У меня все прошло без ошибок. Надеюсь у вас тоже.
Ну а это структура нашего нового проекта.

  P.S.:
Если вы работаете в WINAVR и ваш проект состоит из нескольких файлов, компилятору нужно указывать их вручную. Делается это в Makefile – просто вписываем название файлов через пробел.
# List C source files here. (C dependencies are automatically generated.)
SRC = $(TARGET).c lcd_lib.c
Файлы
Проект для IARa. Проект для WINAVR.
 
											
Comments
#define port_rs PORTB_PORTB5
соответственно :
port_rs = 0;
port_rs = 1;
( iar avr 5.40 )
пример объявления;
extern volatile unsigned char raz;
extern unsigned char keyState;
номера битов порта вывода. Т.е.
#define RS 4
#define RW 5
#define EN 6
либо править схему. Повнимательней, пожалуйста. Начинающим довольно трудно выцеплять такие баги.
А в целом - молодца!!! Хороший материал.
Quote:Но в lcd_lib.h нет упоминания о файле lcd_lib.с. Как компилятор "узнает", что исходный текст в последнем и его надо обрабатывать?
цитата:
Добавляем файл lcd_lib.c в наш проект. (Правой кнопкой мышки кликаем в workspace`e и в открывшемся меню выбираем Add > Add files…).
А непосредственно в самом хэдере мы о нем ничего не упоминаем. Так и надо? Или все же есть способ, без использования правой кнопки мыши?
Code:
#include "LCD.c"
А в Codevision-е видны. Отличный от других?
- упорядоченная структура программы,
- отделение интерфейса модуля от его реализации,
- возможность повторного использования кода.
Теоретически да, нужно перекомпилирова ть один модуль. Но потом нужно еще собрать весь проект (этим занимается линкер).
Можно перенести, но это равносильно написанию кода в одном файле.
Ну примерно так
есть интерфейс i2c - для него написана библиотека, которая реализует функции для работы с ним.
есть микросхема DS1307 - для нее написана своя библиотека, которая реализует функции для работы с ней и из нее вызывается библиотека для i2c.
Также можно для spi, 1-wire.
RSS feed for comments to this post