Создание проекта для AVR: файлы Makefile и CMakeLists.txt, использование Qt Creator в качестве AVR IDE, библиотека Procyon AVRlib

разделы: среда разработки , дата: 20 октября 2015г.

Если посмотреть на исходник в предыдущем посте, то можно заметить, что программная часть работы с 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

скачать архив с исходниками можно здесь.