Topic: tech dcc pc src prev next
tech dcc pc src > cfg.c
#include "cfg.h"
#include <avr/io.h>
#include <avr/eeprom.h>
#include "constants.h"
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0]))
#define CFG_FILE_LENGTH 2048
cfg_loco EEMEM e_locomotive_list[CFG_LOCO_LIST_LENGTH];
cfg_address_type EEMEM e_accessory_list[CFG_ACCESSORY_LIST_LENGTH + 1];
cfg_address_type EEMEM e_accessory_on_list[CFG_ACCESSORY_LIST_LENGTH + 1];
cfg_address_type EEMEM e_accessory_hotlist[8];
// Bubble sort a list into ascending order. The end of the list is denoted by
// CFG_NULL_ADDR.
void bubble_sort(cfg_address_type *list)
{
size_t length = 0;
while(list[length] != CFG_NULL_ADDR)
++length;
// Bubble sort the list.
for(cfg_loco_index_type i = 0; length && i < length - 1; ++i)
{
for(cfg_loco_index_type ii = 0; ii < length - i - 1; ++ii)
{
if(list[ii] > list[ii + 1])
{
cfg_address_type tmp = list[ii];
list[ii] = list[ii + 1];
list[ii + 1] = tmp;
}
}
}
}
void Cfg_Reset()
{
// EEPROM is written in 8-byte pages. Check if each page is clear; if not,
// write the default value (0xFF) to each byte in the page.
const uint8_t clear[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t data[8];
for(unsigned int addr = 0; addr < CFG_FILE_LENGTH; addr += 8)
{
eeprom_read_block((void *)&data[0], (const void *)addr, 8);
for(unsigned char i = 0; i < 8; ++i)
{
if(data[i] != 0xFF)
{
eeprom_write_block((const void*)&clear[0], (void*)addr, 8);
break;
}
}
}
// Add one locomotive with address 3.
cfg_loco default_loco = Cfg_DefaultLoco();
default_loco.address = 3;
Cfg_WriteLocoToIndex(0, default_loco);
}
bool Cfg_LocoAddressValid(cfg_address_type address)
{
return (address > 0 && address <= HERMES_MAX_LOCO_ADDRESS);
}
unsigned char Cfg_LocoFunctions(const cfg_loco *loco)
{
unsigned char count = 0;
functions_type f = loco->f_enable;
for(unsigned char i = 0; i < 32; ++i)
{
if(f & 1)
++count;
f >>= 1;
}
return count;
}
bool Cfg_AccessoryAddressValid(cfg_address_type address)
{
return (address > 0 && address <= HERMES_MAX_ACC_ADDRESS);
}
cfg_loco_index_type Cfg_AddressCount(
const cfg_address_type *list
)
{
cfg_loco_index_type count = 0;
while(list[count] != CFG_NULL_ADDR)
++count;
return count;
}
cfg_loco_index_type Cfg_IndexInList(
cfg_address_type addr,
const cfg_address_type *list
)
{
for(cfg_loco_index_type index = 0; list[index] != CFG_NULL_ADDR; ++index)
{
if(list[index] == addr)
return index;
}
return CFG_NULL_INDEX;
}
void Cfg_AddToList(
cfg_address_type addr,
cfg_address_type *list
)
{
while(*list != CFG_NULL_ADDR && *list < addr)
++list;
if(*list == addr)
return;
// *list > addr
// Move every element in the list back by one space.
cfg_address_type tmp = *list;
*list = addr;
++list;
do
{
const cfg_address_type tmp2 = *list;
*list = tmp;
tmp = tmp2;
++list;
}
while(tmp != CFG_NULL_ADDR);
*list = CFG_NULL_ADDR;
}
void Cfg_RemoveFromList(
cfg_address_type addr,
cfg_address_type *list
)
{
while(*list != CFG_NULL_ADDR && *list != addr)
++list;
if(*list == CFG_NULL_ADDR)
// 'addr' was not found.
return;
// Move each item one place towards the start of the list.
do
{
*list = *(list + 1);
++list;
}
while(*list != CFG_NULL_ADDR);
}
cfg_address_type Cfg_NextInList(
cfg_address_type *const list,
cfg_address_type current
)
{
for(int index = 0; list[index] != CFG_NULL_ADDR; ++index)
{
if(list[index] == current)
{
// Return the next item in the list (wrapping if required).
return list[(index + 1) % Cfg_AddressCount(list)];
}
}
// The item was not found in the list.
return CFG_NULL_ADDR;
}
cfg_address_type Cfg_PrevInList(
cfg_address_type *const list,
cfg_address_type current
)
{
for(int index = 0; list[index] != CFG_NULL_ADDR; ++index)
{
if(list[index] == current)
{
// Return the previous item in the list (wrapping if required).
return list[(Cfg_AddressCount(list) + index - 1) % Cfg_AddressCount(list)];
}
}
// The item was not found in the list.
return CFG_NULL_ADDR;
}
cfg_loco Cfg_DefaultLoco()
{
cfg_loco out = {
.address = CFG_NULL_ADDR,
.speed_steps = SPEED_STEPS_128,
.stop_mode = STOP_MODE_ON_ENTERING_STATION,
.f_default = 0,
.f_enable = 0,
.f_momentary = 0,
.f_list = {
CFG_NULL_FN, CFG_NULL_FN, CFG_NULL_FN, CFG_NULL_FN,
CFG_NULL_FN, CFG_NULL_FN, CFG_NULL_FN, CFG_NULL_FN
}
};
return out;
}
cfg_loco Cfg_ReadLoco(cfg_address_type address)
{
cfg_loco_index_type index = Cfg_AddrToIndex(address);
if(index == CFG_NULL_INDEX)
return Cfg_DefaultLoco();
return Cfg_ReadLocoFromIndex(index);
}
cfg_loco Cfg_ReadLocoFromIndex(cfg_loco_index_type index)
{
cfg_loco out;
if(index == CFG_NULL_INDEX)
out = Cfg_DefaultLoco();
else
eeprom_read_block(&out, &e_locomotive_list[index], sizeof(cfg_loco));
return out;
}
bool Cfg_WriteLoco(cfg_loco loco)
{
cfg_loco_index_type index = Cfg_AddrToIndex(loco.address);
if(index == CFG_NULL_INDEX)
index = Cfg_NextFreeLoco();
if(index == CFG_NULL_INDEX)
return false;
Cfg_WriteLocoToIndex(index, loco);
return true;
}
void Cfg_WriteLocoToIndex(
cfg_loco_index_type index,
cfg_loco loco
)
{
eeprom_update_block(&loco, &e_locomotive_list[index], sizeof(cfg_loco));
}
void Cfg_Addresses(cfg_address_type *list)
{
cfg_loco_index_type i_out = 0;
for(cfg_loco_index_type i = 0; i < CFG_LOCO_LIST_LENGTH; ++i)
{
cfg_loco loco = Cfg_ReadLocoFromIndex(i);
if(loco.address != CFG_NULL_ADDR)
list[i_out++] = loco.address;
}
list[i_out] = CFG_NULL_ADDR;
bubble_sort(list);
}
cfg_loco_index_type Cfg_NextFreeLoco()
{
for(cfg_loco_index_type i = 0; i < CFG_LOCO_LIST_LENGTH; ++i)
{
cfg_loco loco = Cfg_ReadLocoFromIndex(i);
if(loco.address == CFG_NULL_ADDR)
return i;
}
return CFG_NULL_INDEX;
}
cfg_loco_index_type Cfg_AddrToIndex(cfg_address_type address)
{
for(cfg_loco_index_type i = 0; i < CFG_LOCO_LIST_LENGTH; ++i)
{
cfg_loco loco = Cfg_ReadLocoFromIndex(i);
if(loco.address == address)
return i;
}
return CFG_NULL_INDEX;
}
void Cfg_Accessories(cfg_address_type *list)
{
cfg_loco_index_type i_out = 0;
for(cfg_loco_index_type i = 0; i < CFG_ACCESSORY_LIST_LENGTH; ++i)
{
cfg_address_type address = eeprom_read_word(&e_accessory_list[i]);
if(address != CFG_NULL_ADDR)
list[i_out++] = address;
}
list[i_out] = CFG_NULL_ADDR;
bubble_sort(list);
}
void Cfg_SaveAccessory(cfg_address_type accessory_address)
{
for(size_t i = 0; i < CFG_ACCESSORY_LIST_LENGTH; ++i)
{
cfg_address_type address = eeprom_read_word(&e_accessory_list[i]);
if(address == accessory_address)
return;
}
for(size_t i = 0; i < CFG_ACCESSORY_LIST_LENGTH; ++i)
{
cfg_address_type address = eeprom_read_word(&e_accessory_list[i]);
if(address == CFG_NULL_ADDR)
{
eeprom_write_word(
&e_accessory_list[i],
(unsigned int)accessory_address
);
return;
}
}
}
void Cfg_DeleteAccessory(cfg_address_type accessory_address)
{
for(size_t i = 0; i < CFG_ACCESSORY_LIST_LENGTH; ++i)
{
cfg_address_type address = eeprom_read_word(&e_accessory_list[i]);
if(address == accessory_address)
eeprom_write_word(&e_accessory_list[i], CFG_NULL_ADDR);
}
}
void Cfg_ReadAccessoryOn(cfg_address_type *accessories)
{
for(unsigned char i = 0; i <= CFG_ACCESSORY_LIST_LENGTH; ++i)
{
accessories[i] = eeprom_read_word(&e_accessory_on_list[i]);
if(accessories[i] == CFG_NULL_ADDR)
break;
}
}
void Cfg_SaveAccessoryOn(cfg_address_type *accessories)
{
for(unsigned char i = 0; i <= CFG_ACCESSORY_LIST_LENGTH; ++i)
{
// Write the accessory address
eeprom_write_word(&e_accessory_on_list[i], accessories[i]);
if(accessories[i] == CFG_NULL_ADDR)
break;
}
}
void Cfg_ReadAccessoryHotlist(cfg_address_type *accessories)
{
eeprom_read_block(
(void *)accessories,
(const void *)e_accessory_hotlist,
sizeof(cfg_address_type) * 8
);
}
void Cfg_SaveAccessoryHotlist(cfg_address_type *accessories)
{
eeprom_update_block(
(void *)accessories,
(void *)e_accessory_hotlist,
sizeof(cfg_address_type) * 8
);
}