Начал возиться с прошивкой.
Для начала попытался реализовать передачу пакета с данными (выстрел).
Единственное - так нигде и не нашёл, какие биты должны передаваться первыми - старшие или младшие?
Так что не исключено, что получился "зеркальный" Miles Tag.
Вот чего накропел:
файл types.h
Код:
#ifndef bool
#define bool unsigned char
#define true 1
#define false 0
#endif
//---------------------------------------------------------------------//
enum Team_Color {Red, Blue, Yellow, Green}; //Определим перечисляемый тип
typedef enum Team_Color tteam_color; //для работы с цветом команды
/*
00 = Red
01 = Blue
10 = Yellow
11 = Green
*/
//---------------------------------------------------------------------//
//Определим перечисляемый тип
//для работы с "уроном"
enum GunDamage { Damage_1, Damage_2, Damage_4, Damage_5,
Damage_7, Damage_10, Damage_15, Damage_17,
Damage_20, Damage_25, Damage_30, Damage_35,
Damage_40, Damage_50, Damage_75, Damage_100};
typedef enum GunDamage tgun_damage;
/*
0000 = 1
0001 = 2
0010 = 4
0011 = 5
0100 = 7
0101 = 10
0110 = 15
0111 = 17
1000 = 20
1001 = 25
1010 = 30
1011 = 35
1100 = 40
1101 = 50
1110 = 75
1111 = 100
*/
//---------------------------------------------------------------------//
//Определим структуру для хранения идентификатора игрока
//в ней будем хранить длительность импулсов в "тиках" таймера (IR_ZERO или IR_ONE)
typedef struct PlayerID {
uint8_t bit_0; //Первый бит (всегда должен быть равен IR_ZERO)
uint8_t bit_1;
uint8_t bit_2;
uint8_t bit_3;
uint8_t bit_4;
uint8_t bit_5;
uint8_t bit_6;
uint8_t bit_7;//последний бит
} tplayer_id;
/*
union player_id_union {
tplayer_id bits;
uint8_t data [8];
};
*/
//Определим структуру для хранения идентификатора (цвета) команды
//в ней будем хранить длительность импулсов в "тиках" таймера (IR_ZERO или IR_ONE)
typedef struct TeamID{
uint8_t bit_0;
uint8_t bit_1;
} tteam_id;
//Определим структуру для хранения урона
//в ней будем хранить длительность импулсов в "тиках" таймера (IR_ZERO или IR_ONE)
typedef struct Damage{
uint8_t bit_0;
uint8_t bit_1;
uint8_t bit_2;
uint8_t bit_3;
} tdamage;
//Теперь опишем структуру пакета данных
typedef struct DataPacket {
uint8_t header; //заголовок, всегда должен быть равен IR_START (1 Байт)
tplayer_id player_id; //после заголовка идет идентификатор игрока (8 Байт)
tteam_id team_id; //Затем идентификатор команды (2 Байта)
tdamage damage; //Ну и последним стоит "урон" (4 Байта)
uint8_t end_of_data; //Метка, указывающая передатчику, что данных для отправки больше нет (всегда должна быть равна 0) (1 Байт)
} tdata_packet;
//---------------------------------------------------------------------//
union data_packet_union{
tdata_packet packet;
uint8_t data[16];
};
файл firmware.h
Код:
#include <avr/io.h> /* Определяет имена для портов ввода-ывода */
#include <util/delay.h> /* Дает возможность формирования задержки */
#include <avr/interrupt.h> /* Будем использовать прерывания */
#include "types.h"
#ifndef F_CPU //Этот параметр нужно задавать в свойствах проекта, если там не задан, задаем здесь
#define F_CPU 7372800 // Тактовая частота в Гц
#endif
#define IR_START_BIT_DURATION 2400 // Длительность Старт-Бита (в микросекундах)
#define IR_ONE_BIT_DURATION 1200 // Длительность Бита, соотретствующего единичке (в микросекундах)
#define IR_ZERO_BIT_DURATION 600 // Длительность Бита, соотретствующего нулю (в микросекундах)
#define IR_SPACE_DURATION 600 // Длительность Бита, соотретствующего интервалу между битами (в микросекундах)
#define IR_F0 36000 // Несущая частота ИК-приемника (f0)
#define IR_ZERO (IR_ZERO_BIT_DURATION*2*IR_F0)/1000000 //Длительность импулся, соответствующего биту = 0
//выраженная в "тиках" таймера
#define IR_ONE (IR_ONE_BIT_DURATION*2*IR_F0)/1000000 //Длительность импулся, соответствующего биту = 1
//выраженная в "тиках" таймера
#define IR_START (IR_START_BIT_DURATION*2*IR_F0)/1000000 //Длительность заголовка
//выраженная в "тиках" таймера
#define IR_SPACE (IR_SPACE_DURATION*2*IR_F0)/1000000 //Длительность интервала между передоваемыми битами
//выраженная в "тиках" таймера
#define DEFAULT_PLAYER_ID 1 //Идентификатор игрока по умолчанию
#define DEFAULT_TEAM_COLOR Red //Цвет команды по умолчанию (команда красных)
#define DEFAULT_DAMAGE Damage_1 //Урон по умолчанию (урон по умолчанию равен 1)
volatile static bool ir_transmitter_on;//флаг, разрешаюший (true) или запрещающий передачу данных через ИК-диод
//Декларируем наши функции
void init_timer2(void); //Настройка таймера
void init_var(void); //Присваиваем дефалтовые значения переменным
void send_ir_package(void); //Отправляем подготовленный пакет (выстрел)
void set_player_id(uint8_t); //Задаем игроку идентификатор
void set_team_color(tteam_color); //Задаем цвет нашей команды
void set_gun_damage(tgun_damage); //Задаем мощьность нашего оружия (урон)
//Глобальные переменные
volatile static int ir_pulse_counter; //Обратный счетчик "вспышек" ИК-диода
volatile static int ir_space_counter; //Обратный счетчик длительности выключенного состояния ИК-диода (пауза между битами)
static volatile uint8_t prt; //В эту переменную будем считывать состояние порта, на котором висит ИК-приемник
static volatile union data_packet_union data_packet; //В этой переменной будем формировать пакет данных для отправки через IR
static volatile uint16_t timer1; //Используется как таймер для создания временных задержек (увеличивается на 1 после каждого прерываня таймера)
static volatile uint8_t cursor_position;//Эта переменная будет хранить индех элемента массива данных data_packet.data[x]
//который нужно передать следующим
файл ltagfirmware.c
Код:
#include "firmware.h"
int main (void) { // Начало выполнения программы, главная функция
DDRA |= (1 << 0)|(1<<5)|(1<<7); // Устанавливаем порт PORTA.1 как выход
DDRD &=~(1<<2); //настраиваем вывод INT0 как вход
init_timer2(); // Настраиваем таймер timer2
init_var(); //Задаём значения переменным
sei();
while(1){
send_ir_package(); //Производим "выстрел"
//_delay_ms(1000); // секундная задержка
timer1=0; //Сделаем паузу
while(timer1 < 65000); //Подождем, пока не произойдет 65000 прерываний таймера (чуть меньше секунды)
PORTA ^= (1<<5); //На этой ноге висит вспомогательный светодиод, просто меняет свое состояние в такт с выстрелами
}
return 0; /* Выход из программы, в данном случае останов который */
/* никогда не произойдет т.к. выше бесконечный цикл */
} //end main
/**************************************************************************************
* Функция выполняет настройку таймера timer2
* Режим работы таймер - СТС (сброс при совпадении)
* тактирование - без делителя, с частотой кварца
* регист сравнения будет иметь такое значение, чтобы прерывания
* генерировались с удвоенной частотой несущей ИК-приемника
***************************************************************************************/
void init_timer2(void){
OCR2 = F_CPU/IR_F0/2-1; //Прерывания будут генерироваться с частотой, вдвое большей, чем частота несущей
TCCR2 = _BV(CS20)|_BV(WGM21); // Режим работы таймер - СТС (сброс при совпадении)
// Тактирование с частотой резонвтора (7 372 800 Гц)
TIMSK |= _BV(OCIE2); // Разрешаем прерывания по захвату/сравнению
}
void init_var(void){ //Задаём значения переменным
ir_transmitter_on=false; //Запретим пока передачу данных (поскольку данные ещё не сформированны)
set_player_id(DEFAULT_PLAYER_ID); //Устанавливаем идентификатор игрока
set_team_color(DEFAULT_TEAM_COLOR); //Устанавливаем идентификатор (цвет) команды
set_gun_damage(DEFAULT_DAMAGE); //Устанавливаем мощьность оружия (урон)
data_packet.packet.header = IR_START; //Устанавливаем заголовку (старт биту) необходимую длительность
data_packet.packet.end_of_data = 0; //0 - это указание передатчику, что данных для передачи больше нет
cursor_position = 0; //Курсор - на начало блока данных
}
ISR(TIMER2_COMP_vect){
timer1++;
prt = PIND&(1<<2); // опрашиваю приемник
if (prt==0) PORTA &= ~(1 << 0); // если ловит несущую, включаю диод (вспомогательный)
else PORTA |=(1<<0); // если не ловит несущую - тушу диод (вспомогательный)
if (ir_transmitter_on==true)
{//Если передача разрешена
if (ir_pulse_counter > 0) //необходимая длительность пачки импульсов
{ //ещё не достигнута, "мигаем" дальше
PORTA ^= (1<<7); //необходимая длительность пачки
ir_pulse_counter--;
}
else //пачка импульсов была отправлена,
{
PORTA &=~(1<<7); //тушим ИК-диод
if ( ir_space_counter > 0) //проверим, выдержан ли промежуток между импульсами
{ //нет, промежуток не выдержан
PORTA &=~(1<<7); //тушим ИК-диод
ir_space_counter--; //уменьшаем обратный счетчик паузы на один "тик"
}
else //Пакет импульсов и промежуток между битами переданы
{ //нужно формировать следующую пачку (передаваемый бит)
if (data_packet.data[cursor_position]!=0) //если указатель указывает не на пустую ячейку
{
ir_pulse_counter =data_packet.data[cursor_position++] ; //передадим импульс указанной длительностью
ir_space_counter = IR_SPACE; //и про паузу не забудем
}
else //Все данные переданы (элемент, на который ссылается указатель, равен 0)
{
ir_transmitter_on=false; //выключаем передатчик
}
}
}
}
else {//Если передача запрещена
}
}
/**************************************************************************************
* Функця производит "выстрел"
* устанавлвает курсор на позицию начала блока данных data_packet.data[0]
* и разрешает передачу данных
* функция возвращает управление только после отправки всех данных
***************************************************************************************/
void send_ir_package(void){ //Отправляем пакет ("стреляем")
cursor_position = 0; //Курсор - на начало блока данных
ir_transmitter_on = true; //Разрешаем передачу
while (ir_transmitter_on); //Ждем, пока пакет отправиться
}
/**************************************************************************************
* Установка идентификатора игрока
* в качестве аргумента функции указывается идентификационный номер игрока (от 1 до 127)
* в результате выполнения функции в глобальной переменной data_packet.player_id
* будут соответствующим образом инициированы data_packet.player_id.(bit_0 ... bit_7)
***************************************************************************************/
void set_player_id(uint8_t ID){
uint8_t *p_id_bit; //указатель на биты структуры player_id
p_id_bit = &data_packet.packet.player_id.bit_7; //указывает на 7 "бит" структуры
for (int i=0; i < 7; i++) { //надо узнать значения 7 младших бит ID
ID = ID << 1; //сдвигаем влево на один бит
if (ID&(1<<7)) //если старший бит = 1
{
*p_id_bit-- = IR_ONE; //присваиваем соответствующее значение data_packet.player_id.bit_x
}
else
{
*p_id_bit-- = IR_ZERO;
}
}
data_packet.packet.player_id.bit_0 = IR_ZERO; //согласно протоколу, этот "бит" должен быть равен 0
}
/**************************************************************************************
* Установка идентификатора (цвета) команды
* в качестве аргумента функции указывается идентификационный номер (цвет) команды (от 0 до 3)
* в результате выполнения функции в глобальной переменной data_packet.team_id
* будут соответствующим образом инициированы data_packet.team_id.(bit_0 и bit_1)
***************************************************************************************/
void set_team_color(tteam_color color){
switch(color){
case Red : { //По протоколу 00 = Red
data_packet.packet.team_id.bit_0 = IR_ZERO;
data_packet.packet.team_id.bit_1 = IR_ZERO;
break;
}
case Blue: { //По протоколу 01 = Blue
data_packet.packet.team_id.bit_0 = IR_ONE;
data_packet.packet.team_id.bit_1 = IR_ZERO;
break;
}
case Yellow: { //По протоколу 10 = Yellow
data_packet.packet.team_id.bit_0 = IR_ZERO;
data_packet.packet.team_id.bit_1 = IR_ONE;
break;
}
case Green: { //По протоколу 11 = Green
data_packet.packet.team_id.bit_0 = IR_ONE;
data_packet.packet.team_id.bit_1 = IR_ONE;
break;
}
}
}
/**************************************************************************************
* Установка установка мощьности нашего оружия (наносимый урон)
* в качестве аргумента функции указывается наносимый урон
* в результате выполнения функции в глобальной переменной data_packet.damage
* будут соответствующим образом инициированы data_packet.damage.(bit_0 и bit_3)
***************************************************************************************/
void set_gun_damage(tgun_damage damage){
switch(damage){
case Damage_1:{ //По протоколу 0000 = 1
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_2:{ //По протоколу 0001 = 2
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_4:{ //По протоколу 0010 = 4
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_5:{ //По протоколу 0011 = 5
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_7:{ //По протоколу 0100 = 7
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_10:{ //По протоколу 0101 = 10
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_15:{ //По протоколу 0110 = 15
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_17:{ //По протоколу 0111 = 17
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ZERO;
break;
}
case Damage_20:{ //По протоколу 1000 = 20
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_25:{ //По протоколу 1001 = 25
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_30:{ //По протоколу 1010 = 30
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_35:{ //По протоколу 1011 = 35
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ZERO;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_40:{ //По протоколу 1100 = 40
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_50:{ //По протоколу 1101 = 50
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ZERO;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_75:{ //По протоколу 1110 = 75
data_packet.packet.damage.bit_0 = IR_ZERO;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
case Damage_100:{ //По протоколу 1111 = 100
data_packet.packet.damage.bit_0 = IR_ONE;
data_packet.packet.damage.bit_1 = IR_ONE;
data_packet.packet.damage.bit_2 = IR_ONE;
data_packet.packet.damage.bit_3 = IR_ONE;
break;
}
}
}
Вопросы, замечания, пожелания?
У кого есть "камень" (Atmega16) под рукой, залейте, попробуйте, отпишитесь.
Жаль, осцилогрофа у меня нет.
И на реальном Милесе бы проверить, вдруг биты не в той последовательности передаются.
Сечас буду маны курить по поводу внешних прерываний.
Как прием организовать - пока не определился, есть пара вариантов, какой лучше - не знаю.