#4 из чего состоит проект и что в файлах?
Август 17, 2008
Основы для первого проекта. Типы файлов и информация в них.
Простой проект состоит из одного файла с расширением .c .
Однако даже простой проект может содержать в себе много кода и этот код может быть универсальным.
Например я использую в проекте 2х строчный жк индикатор. А у вас его нет, но есть однострочный.
Что делать? Достаточно в одном файле изменить один параметр и пересобрать.
Это файл заголовка .h.
В этом файле определяются разные константы и макросы, а так же прототипы функций.
По моем мнению прототипы не нужны и не обязательны.
Они служат только для быстрого определения типов данных в функциях от третьих разработчиков.
Гцц вроде бы выдает только предупреждение об отсутствии прототипов.
Давайте разберем обычный .с файл.
На первом месте стоит комментарий с краткой информацией о проекте и данных автора.
Комментарии бывают 2х видов: однострочный и многострочный.
// однострочный
/* а это многострочный комментарий
и он начинается с одного слеша и звездочки,
а заканчивается наоборот
*/
На втором месте идут инклюды - команды препроцессора.
Стандартный инклюд любого проекта под гцц выглядит так:
# include <avr/ io.h>
Это значит, что препроцессор должен взять текст из файла io.h и поместить в главный файл проекта. И только потом он начинает проверять весь синтаксис.
Таких инклюдов может быть несколько и в разных .с файлах. Даже одинаковый заголовок может быть включен в каждый .с файл.
Инклюды есть 2х типов: родные и неродные.
Неродные - это как раз ваш .h, который должен лежать в папке проекта
Подключается он так:
#include "myheader.h"
Что есть в тех файлах и как оно выглядит?
#define LCD_IO_MODE 6 /* 6=6 PIN I/O, 2=2 PIN I/O, 3=I2C, 7=multi lcd */
#define LCD_AUTO_LINE_FEED 0 /* 1 = Auto line feed, 0 = no Auto line feed */
#define LCD_DELAY_TIME_US 100 /* THE TYPICAL TIME THE LCD NEEDS TO COMPLETE A COMMAND */
#define LCD_E_PULSE_WIDTH_US 1 /* THE E PULSE WIDTH IN MICROSECONDS >Timing is accurate)*/
#define LCD_DECIMAL_POINT '.' /* The decimal point punctuation mark char */
После инклюдов идут дефайны, которые уже показаны выше.
В простом проекте такие дефайны можно писать прям в .с файле.
После инклюдов идут глобальные переменные. Эти переменные могут быть доступны из главной функции или из обработчика прерываний.
Обработчики прерываний - следущая часть программы. Их может и не быть.
После них можно писать тела функций.
Или функции можно писать в самом низу, после главной функции.
Обработчик прерывания выглядит так:
INTERRUPT(SIG_OVERFLOW0)
{
.... код ....
}
Если инклюд был обязательным, то все выше перечисленные часть необязательно должны быть.
Обязательная часть любой программы - главная функция с названием main.
Эта функция не принимает параметров и не выдает результат. Посему тип данных будет void.
Записывается это так:
void main(void){
... код...
}
После последней скобки надо пропустить одну строку. С высоты 2008 года это выглядит смешно, а лет 10-20 назад это был стандарт.
Хотя это необязательно.
Вот так выглядит главный файл одного из проектов.
/* PLL synteza SAA1057, LCD display */#include <avr/io.h> #include <avr/pgmspace.h> #include <avr/eeprom.h> #include "lcd_io.h" #include <avr/signal.h> #include <avr/interrupt.h>
#define DLEN_ON sbi(PORTD,PD3) #define DATA_ON sbi(PORTD,PD4) #define DLEN_OFF cbi(PORTD,PD3) #define DATA_OFF cbi(PORTD,PD4) #define CLK {sbi(PORTD,PD2);delay(1);cbi(PORTD,PD2);} #define UP ((PINB & 0x01)!=0x01) #define DOWN ((PIND & 0x20)!=0x20) #define MENU ((PIND & 0x40)!=0x40) #define sint uint8_t
sint NTIM; // casovac f = cca 20Hz; int OFREQ, FREQ; // frekvencia; sint up=0, down=0, menu=0, emenu=0, rmem=0, nmem = 0, mset=0;
void InitTimer0() { TCNT0 = 0x3d; TCCR0 = 5; TIMSK |= _BV(TOIE0); }
void InitPorts() { cbi(DDRB, DDB0); sbi(DDRB, DDB6); DDRD = 0x1F; // (0<<DDD6) | (0<<DDD5) | (1<<DDD4) | (1<<DDD3) | (1<<DDD2); cbi(PORTB, PB6); }
void PLL_SendW(unsigned int COMM) { DATA_OFF; DLEN_ON; sint i; CLK; for (i=0; i<16; i++) { if (COMM & 0x8000) DATA_ON; else DATA_OFF; CLK; COMM <<= 1; } DLEN_OFF; clk(10); }
void clk(int c) { int p; for (p=0; p<c; p++) CLK; }
void InitPLL() { clk(20); PLL_SendW(0xCC00); //delay(100); PLL_SendW(0xCC00); //delay(100); //PLL_SendW(10700); //delay(100); }
void delay (int ms) { sint i,j; unsigned int l; for (l=0; l<ms;l++) { for (i=0; i<70; i++) for(j=0; j<250;j++) ; } };
INTERRUPT(SIG_OVERFLOW0) { if UP up++; else up = 0; if DOWN down++; else down = 0; if MENU menu++; else menu = 0; if (emenu) { if (up==1) nmem++; if (down==1) nmem--; if (nmem<0) nmem=0; if (nmem>4) nmem=4; if (menu==0) emenu=2; if ((menu==1) && (emenu==2)) {eeprom_write_word(2*nmem,FREQ); emenu=0; rmem=0; mset=1;} lcd_gotoxy(12,1); lcd_puts_P("M"); if (NTIM<10) lcd_puti(nmem,0); else lcd_puts_P(" "); } else { if ((menu==0) && (rmem==1)) { if (++nmem > 4) nmem=0; FREQ = eeprom_read_word(nmem*2); rmem=0; mset=1; } if (menu==1) {rmem=1;} if (menu>=20) {emenu = 1; NTIM=0;} if (up==1) {FREQ+=5; mset=0;} if (up>=20) {FREQ+=10; mset=0;} if (down==1) {FREQ-=5; mset=0;} if (down>=20) {FREQ-=10; mset=0;} if (FREQ<8800) {FREQ=8800; mset = 0;} if (FREQ>10800) {FREQ=10800; mset = 0;} if ((OFREQ!=FREQ) && (menu==0) && (up==0) && (down==0)) { OFREQ=FREQ; PLL_SendW(FREQ); } lcd_gotoxy(1,1); if (FREQ<10000) lcd_puts_P(" "); lcd_puti(FREQ,2); lcd_puts_P(" MHz"); if (mset) { lcd_puts_P(" M"); lcd_puti(nmem,0); } else lcd_puts_P(" "); } if (++NTIM==20) {NTIM = 0;} }
void main() { InitTimer0(); InitPorts(); InitPLL(); lcd_init(); lcd_clrscr(); lcd_gotoxy(0,0); lcd_puts_P( " PLL SYNTHESIS\n" ); lcd_puts_P( " v1.0 by Dusan" ); delay(2000); //while(1) {;} FREQ = eeprom_read_word(nmem*2); mset = 1; nmem=0; lcd_gotoxy(0,1); lcd_puts_P( " " ); sei(); while(1) {; } }
категория: учим мк avr
вы вот пишите
цитата:
"После них можно писать тела функций.
Или функции можно писать в самом низу, после главной функции."
но ведь в случае если мы будем писать тела ф-ций после main () то нам до главной ф-ции надо написать прототипы остальных ф-ций.
по идее надо, но некоторые компиляторы на это забивают
меня давно мучает вопрос как правильно организовать многомодульную программу
допустим у меня кроме основного модуля main.c есть еще два: saund.c и UART.c
насколько я знаю, то в основном модуле я должен включить хидеры вышеупомянутых модулей
но что я должен писать в этих хидерах ?
хедер необязателен для них
чтобы компилятор видел другие части, они должны быти включены в проект и потом просто вызываеш функции этих модулей в главной функции
возможно там будет ошибка при компиляции, тогда надо перед типом функций в модулях еще поставить extern
например extern void uart();
тогда компилятор будет знать, что это функция внешняя, но вроде бы это тоже устаревшее и некоторые компиляторы и так понимают что где и как
а если мне надо, к примеру, из модуля saund вызвать ф-цию которая находится в модуле UART .
как тогда быть ?
ну так и вызывай просто