Если посмотреть на исходник в предыдущем посте, то можно заметить, что программная часть работы с UART "кочует" из поста в пост, и правильно было бы выделить ее в отдельный файл, как впрочем и реализацию работы с TWI модулем. Развивая мысль, мы придем к необходимости, какой-то библиотеки, куда можно будет скидывать собственные наработки по работе с тем или иным модулем. По правде говоря такоя библиотека уже есть, называется: Procyon AVRlib. Изучение ее безусловно всем рекомендую, но... она стара, как баба-яга, а библиотека для микроконтроллера не шибко сложная вещь, и вероятно каждый, по мере изучения avr-архитектуры, захочет написать собсвенный аналог "с трехмерными шахматами и нейронными диструктурами".
Итак, цель надеюсь понятна, для начала напишем файл проекта Makefile, это будет попроще. Затем, что бы код для AVR можно было писать в Qt Creator, сделаем еще CMakeLists.txt
В качестве "подопытного" кода возмем пример из предыдущего поста. Выделим из него модули UART и TWI, тогда сруктура директорий будет примерно такой:
├── examples │ └── RTC_DS1307 │ ├── CMakeLists.txt │ ├── Makefile │ └── main.c └── libs ├── twi_hw.c ├── twi_hw.h ├── uart.c └── uart.h
где директорию examples вполне логично можно было бы переименовать, например, в projects.
Теперь начинаем "отделять мух от котлет":
в файле uart.h объявляем функции инициализации и uart_putchat.
#include <stdio.h> inline void init_uart(); void blink13(uint8_t count); uint8_t uart_putchar(char c, FILE *stream);
blink13 следовало бы засунуть тоже в отдельный файл, но пока пусть побудет тут. Файл uart.c на мой взгляд не содержит сюрпризов:
#include <avr/io.h> #include <util/delay.h> #include <stdio.h> inline void init_uart() { UBRRL=103; // bitrate 9600 on 16MHz UCSRB=(1<<TXEN)|(1<<RXEN)|(1<<RXCIE); UCSRC=(1<<URSEL)|(3<<UCSZ0); DDRB |= (1<<PB5); // pinMode(13,OUTPUT); } void blink13(uint8_t count) { PORTB |= (1<<PB5); count =(count <<1);count--; //count=(count*2)-1; uint8_t i; for (i=0;i<count;i++) { _delay_ms(500); PORTB ^= (1<<PB5); }; }; uint8_t uart_putchar(char c, FILE *stream) { if (c == '\n') uart_putchar('\r', stream); loop_until_bit_is_set(UCSRA, UDRE); UDR = c; return 0; }
файл twi_hw.h содержит именованные константы и объявления функций:
#define START 0x08 //START has been transmitted #define REP_START 0x10 #define MT_DATA_ACK 0x28 //Master Transmitter staus codes #define MT_ADR_ACK 0x18 //SLA+W has been tramsmitted and ACK received #define MT_ADR_NACK 0x20 //SLA+W has been tramsmitted and NACK received #define MT_DATA_ACK 0x28 //Data byte has been tramsmitted and ACK received #define MT_DATA_NACK 0x30 //Data byte has been tramsmitted and NACK received #define MT_ARB_LOST 0x38 //Arbitration lost in SLA+W or data bytes #define WRITE 0x00 #define READ 0x01 #define READ_END 0x01 #define READ_NOEND 0x00 #define RTC 0xD0 uint8_t read_i2c(uint8_t END); uint8_t send_i2c(uint8_t value); uint8_t start_i2c(uint8_t d_adr); inline void stop_i2c(); inline void init_twi();
файл twi_hw.c как не сложно догадаться будет содержать реализацию работы с аппаратным TWI модулем:
#include <util/twi.h> #include <twi_hw.h> inline void init_twi() { TWBR = (F_CPU / 100000UL - 16)/2; // TWI bitrate } uint8_t send_i2c(uint8_t value) { TWDR = value; TWCR = (1<<TWINT) | (1<<TWEN); while(!(TWCR & (1<<TWINT))) {}; return ((TWSR & 0xF8) != MT_ADR_ACK) ? 1 : 0; } uint8_t start_i2c(uint8_t d_adr) { TWCR=(1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START while (!(TWCR & (1<<TWINT))) {}; uint8_t twst; twst = (TWSR & 0xF8); // check value of TWI Status Register. Mask prescaler bits. if ((twst != START) && (twst != REP_START)) return 1; uint8_t ret; ret=send_i2c(d_adr); return ret; }; inline void stop_i2c() { TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO); } uint8_t read_i2c(uint8_t END) { if (END) TWCR = (1<<TWINT)|(1<<TWEN); else TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while(!(TWCR & (1<<TWINT))); return TWDR; // return data }
тогда main.c уже будет выглядеть более-менее прилично:
#include <avr/io.h> #include <uart.h> #include <twi_hw.h> static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); int main(void) { init_uart(); init_twi(); DDRB |= (1<<PB5); // pinMode(13,OUTPUT); blink13(3); //ready indication stdout = &mystdout; printf("Set address rtc1307: 0x00\n"); if (!start_i2c(RTC)) { uint8_t ret; ret=send_i2c(0x00); stop_i2c(); if (!ret) printf("Set Address success!\n"); uint8_t k, data; k=0; start_i2c(RTC|READ); for(k=0;k<64;k++) { data=read_i2c(READ_NOEND); printf("address: %d value: %d\n", k,data); } stop_i2c(); } else { stop_i2c(); printf("Set Address failure!\n"); } for (;;){}; return 0; }
осталось составить Makefile, чтобы все это могло собраться одной командой:
MCU=atmega8 OBJCOPY=avr-objcopy CC=avr-gcc CFLAGS=-mmcu=$(MCU) -Os -DF_CPU=16000000UL -Wall -I ../../libs LDFLAGS=-mmcu=$(MCU) -Wall -Os -Werror OBJ=main.o uart.o twi_hw.o TARGET=rtc .PHONY: all clean %.o: ../../libs/%.c $(CC) -c -o $@ $< $(CFLAGS) all: $(OBJ) $(CC) $(LDFLAGS) -o $(TARGET).elf $(OBJ) $(OBJCOPY) -O ihex $(TARGET).elf $(TARGET).hex install: avrdude -p$(MCU) -carduino -P/dev/ttyUSB0 -b19200 -D -Uflash:w:./$(TARGET).hex:i clean: @rm -v *.elf *.hex $(OBJ)
Здесь только замечу, что именованная константа F_CPU которая котрая используется в _delay_ms и _delay_us и которая раньше стояла в начале исходника теперь задается через параметр -D в процессе компиляции. Еще, вероятно, ну и в цели install прошивка прописана через загрузчик Arduino. Cборка:
$ make avr-gcc -mmcu=atmega8 -Os -DF_CPU=16000000UL -Wall -I ../../libs -c -o main.o main.c avr-gcc -c -o uart.o ../../libs/uart.c -mmcu=atmega8 -Os -DF_CPU=16000000UL -Wall -I ../../libs avr-gcc -c -o twi_hw.o ../../libs/twi_hw.c -mmcu=atmega8 -Os -DF_CPU=16000000UL -Wall -I ../../libs avr-gcc -mmcu=atmega8 -Wall -Os -Werror -o rtc.elf main.o uart.o twi_hw.o avr-objcopy -O ihex rtc.elf rtc.hex
Теперь можно написать CmakeLists.txt чтобы с проектом можно было работать в Qt Creator.
cmake_minimum_required(VERSION 2.8) project(rtc_ds1307) include_directories(../../libs) set(POJECT_NAME rtc) set(CMAKE_C_COMPILER avr-gcc) set(CMAKE_OBJCOPY avr-objcopy) set(CMAKE_AVRDUDE avrdude) set(CWARN "-Wall -Wstrict-prototypes") set(CMCU "-mmcu=atmega8") set(COPT "-Os") set(CDEFS "-DF_CPU=16000000UL") set(CFLAGS "${CMCU} ${CDEFS} ${COPT} ${CWARN}") set(CMAKE_C_FLAGS ${CFLAGS}) set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set(SOURCE_EXE main.c ../../libs/uart.c ../../libs/twi_hw.c) set(HEADER uart.h twi_hw.c) add_executable(${PROJECT_NAME} ${SOURCE_EXE}) add_custom_target(download COMMAND ${CMAKE_OBJCOPY} -O ihex ${PROJECT_NAME} ${PROJECT_NAME}.hex COMMAND ${CMAKE_AVRDUDE} -patmega8 -carduino -P/dev/ttyUSB0 -b19200 -D -Uflash:w:./${PROJECT_NAME}.hex:i )
Проверим сначала, все ли нормально. создаем директорию для сборки:
palladium:~/avrlib/examples/RTC_DS1307: mkdir build palladium:~/avrlib/examples/RTC_DS1307: cd build
Запускаем cmake:
cmake .. -- The C compiler identification is GNU 4.8.3 -- The CXX compiler identification is GNU 4.8.3 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /home/flanker/avrlib/examples/RTC_DS1307/build
Затем запускаем make:
palladium:~/avrlib/examples/RTC_DS1307/build: make Scanning dependencies of target rtc_ds1307 [ 33%] Building C object CMakeFiles/rtc_ds1307.dir/main.c.o In file included from /home/flanker/avrlib/examples/RTC_DS1307/main.c:2:0: /home/flanker/avrlib/examples/RTC_DS1307/../../libs/uart.h:3:1: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void init_uart(); ^ In file included from /home/flanker/avrlib/examples/RTC_DS1307/main.c:3:0: /home/flanker/avrlib/examples/RTC_DS1307/../../libs/twi_hw.h:24:1: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void stop_i2c(); ^ /home/flanker/avrlib/examples/RTC_DS1307/../../libs/twi_hw.h:25:1: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void init_twi(); ^ [ 66%] Building C object CMakeFiles/rtc_ds1307.dir/home/flanker/avrlib/libs/uart.c.o /home/flanker/avrlib/libs/uart.c:5:13: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void init_uart() ^ [100%] Building C object CMakeFiles/rtc_ds1307.dir/home/flanker/avrlib/libs/twi_hw.c.o In file included from /home/flanker/avrlib/libs/twi_hw.c:2:0: /home/flanker/avrlib/examples/RTC_DS1307/../../libs/twi_hw.h:24:1: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void stop_i2c(); ^ /home/flanker/avrlib/examples/RTC_DS1307/../../libs/twi_hw.h:25:1: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void init_twi(); ^ /home/flanker/avrlib/libs/twi_hw.c:4:13: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void init_twi() ^ /home/flanker/avrlib/libs/twi_hw.c:31:13: warning: function declaration isn't a prototype [-Wstrict-prototypes] inline void stop_i2c() ^ Linking C executable rtc_ds1307 [100%] Built target rtc_ds1307 palladium:~/avrlib/examples/RTC_DS1307/build: ls -l итого 48 -rw-r--r-- 1 flanker users 12122 окт 20 11:29 CMakeCache.txt drwxr-xr-x 6 flanker users 280 окт 20 11:29 CMakeFiles -rw-r--r-- 1 flanker users 7538 окт 20 11:29 Makefile -rw-r--r-- 1 flanker users 1642 окт 20 11:29 cmake_install.cmake -rwxr-xr-x 1 flanker users 21081 окт 20 11:29 rtc_ds1307
Вылезло несколько предупреждений, но, все собралось без ошибок. прошивку я специально выделил в цель download, что бы Qt Creator не пыталься прошить отключеный микроконтроллер. т.е. прошивка будет из консоли командой:
$ make download
Теперь можно открыть CMakeLists.txt в Qt Creator и продолжить писать код в этой, замечательной во всех отношениях, среде разработки:
этап сборки AVR проекта в IDE QtCreator
скачать архив с исходниками можно здесь.