kopia lustrzana https://github.com/alpov/SatCam
450 wiersze
13 KiB
C
450 wiersze
13 KiB
C
/*************************************************************************
|
|
*
|
|
* SatCam - Camera Module for PSAT-2
|
|
* Copyright (c) 2015-2017 Ales Povalac <alpov@alpov.net>
|
|
* Dept. of Radio Electronics, Brno University of Technology
|
|
*
|
|
* This work is licensed under the terms of the MIT license
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "cube.h"
|
|
#include "comm.h"
|
|
#include "eeprom.h"
|
|
|
|
/* access to global configuration in satcam.c */
|
|
extern CONFIG_SYSTEM config;
|
|
extern const CONFIG_SYSTEM config_default;
|
|
|
|
|
|
static uint8_t i2c_delay_value = 255; // I2C half bit delay derived from HCLK
|
|
static uint32_t auth_tick_end = 0; // end tick of authorization
|
|
|
|
static const char *counter_name[] = {
|
|
[LOG_RST_IWDG] = "rst-iwdg",
|
|
[LOG_RST_WWDG] = "rst-wwdg",
|
|
[LOG_RST_BOR] = "rst-bor",
|
|
[LOG_RST_SFTR] = "rst-sftr",
|
|
[LOG_RST_PIN] = "rst-pin",
|
|
[LOG_HARDFAULT] = "hardfault",
|
|
[LOG_AUDIO_START] = "audio-start",
|
|
[LOG_AUDIO_STOP] = "audio-stop",
|
|
[LOG_CAM_START] = "cam-start",
|
|
[LOG_CAM_STOP] = "cam-stop",
|
|
[LOG_CAM_READY] = "cam-ready",
|
|
[LOG_CAM_SNAPSHOT] = "cam-snapshot",
|
|
[LOG_CAM_I2C_ERROR] = "cam-i2c-error",
|
|
[LOG_CAM_DCMI_ERROR] = "cam-dcmi-error",
|
|
[LOG_CAM_SIZE_ERROR] = "cam-size-error",
|
|
[LOG_JPEG_ERROR] = "jpeg-error",
|
|
[LOG_CMD_HANDLED] = "cmd-handled",
|
|
[LOG_CMD_IGNORED] = "cmd-ignored",
|
|
[LOG_BOOT] = "boot",
|
|
};
|
|
|
|
#define i2c_delay() for (uint8_t __delay = 0; __delay < i2c_delay_value; __delay++) __NOP();
|
|
#define i2c_wscl(__x) EEP_SCL_GPIO_Port->BSRR = EEP_SCL_Pin << ((__x) ? 0 : 16)
|
|
#define i2c_wsda(__x) EEP_SDA_GPIO_Port->BSRR = EEP_SDA_Pin << ((__x) ? 0 : 16)
|
|
#define i2c_rsda() (EEP_SDA_GPIO_Port->IDR & EEP_SDA_Pin)
|
|
|
|
|
|
void HardFault_Handler(void) __attribute__ ((naked)); // otherwise we need to check LR depth in MSP
|
|
void HardFault_Handler(void)
|
|
{
|
|
uint32_t *stack_frame = (uint32_t*)__get_MSP();
|
|
|
|
/* save current CPU state to BKUP memory */
|
|
BKUP->tick = HAL_GetTick();
|
|
BKUP->fault = SCB->CFSR; // Configurable fault status register
|
|
if (BKUP->fault & (1 << 7)) BKUP->addr_fault = SCB->MMFAR; // MMARVALID
|
|
else if (BKUP->fault & (1 << 15)) BKUP->addr_fault = SCB->BFAR; // BFARVALID
|
|
else BKUP->addr_fault = 0;
|
|
BKUP->addr_lr = stack_frame[5]; // Link register from stack
|
|
BKUP->addr_pc = stack_frame[6]; // Program counter from stack
|
|
BKUP->magic = SYSLOG_MAGIC; // hardfault descriptor valid
|
|
|
|
#ifdef ENABLE_SWD_DEBUG
|
|
/* debug output */
|
|
printf_debug("hardfault: fault %#x at %#x, pc %#x, lr %#x",
|
|
(unsigned int)BKUP->fault, (unsigned int)BKUP->addr_fault, (unsigned int)BKUP->addr_pc, (unsigned int)BKUP->addr_lr);
|
|
#endif
|
|
|
|
/* trigger system reset */
|
|
NVIC_SystemReset();
|
|
}
|
|
|
|
|
|
/*
|
|
static uint8_t GetCharHi5(uint16_t Number)
|
|
{
|
|
Number &= 1023;
|
|
uint8_t temp = (Number >> 5); // bits 9..5
|
|
if (temp < 26) return temp + 97; // a..z for 0..25
|
|
else return temp + 39; // A..F for 26..31
|
|
}
|
|
|
|
|
|
static uint8_t GetCharLo5(uint16_t Number)
|
|
{
|
|
Number &= 1023;
|
|
uint8_t temp = (Number & 31); // bits 4..0
|
|
if (temp < 26) return temp + 97; // a..z for 0..25
|
|
else return temp + 39; // A..F for 26..31
|
|
}
|
|
|
|
|
|
static void EncCharFull(uint16_t Number, char **p)
|
|
{
|
|
#if 1
|
|
(*p)[0] = GetCharHi5(Number);
|
|
(*p)[1] = GetCharLo5(Number);
|
|
*p += 2;
|
|
#else
|
|
*p += sprintf_P(*p, PSTR("%u "), Number & 0x03FF);
|
|
#endif
|
|
}
|
|
*/
|
|
|
|
|
|
/* Initialization of the I2C bus interface. Need to be called only once. */
|
|
static void i2c_init(void)
|
|
{
|
|
i2c_wsda(1);
|
|
i2c_wscl(1);
|
|
i2c_delay();
|
|
}
|
|
|
|
|
|
/* Send one byte to I2C device. Returns 0=OK, 1=failed. */
|
|
static uint8_t i2c_write(uint8_t data)
|
|
{
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
i2c_wscl(0); i2c_delay();
|
|
i2c_wsda(data & 0x80); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
data <<= 1;
|
|
}
|
|
i2c_wscl(0); i2c_wsda(1); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
uint8_t result = i2c_rsda() ? 1 : 0;
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Read one byte from the I2C device, ACK as required. */
|
|
static uint8_t i2c_read(bool ack)
|
|
{
|
|
uint8_t data = 0;
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
data <<= 1;
|
|
i2c_wscl(0); i2c_wsda(1); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
data |= i2c_rsda() ? 1 : 0;
|
|
}
|
|
i2c_wscl(0); i2c_wsda(ack ? 0 : 1); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
return data;
|
|
}
|
|
|
|
|
|
/* Issues a start condition and sends address. Returns 0=OK, 1=failed. */
|
|
static uint8_t i2c_start(uint8_t address)
|
|
{
|
|
i2c_delay();
|
|
i2c_wsda(0); i2c_delay();
|
|
return i2c_write(address);
|
|
}
|
|
|
|
|
|
/* Issues a repeated start condition and sends address. Returns 0=OK, 1=failed. */
|
|
static uint8_t i2c_rep_start(uint8_t address)
|
|
{
|
|
i2c_delay();
|
|
i2c_wscl(0); i2c_delay();
|
|
i2c_wsda(1); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
i2c_wsda(0); i2c_delay();
|
|
return i2c_write(address);
|
|
}
|
|
|
|
|
|
/* Terminates the data transfer and releases the I2C bus. */
|
|
static void i2c_stop(void)
|
|
{
|
|
i2c_delay();
|
|
i2c_wscl(0); i2c_wsda(0); i2c_delay();
|
|
i2c_wscl(1); i2c_delay();
|
|
i2c_wsda(1); i2c_delay();
|
|
}
|
|
|
|
|
|
/* Issues a start condition and sends address. Use ACK polling until timeout. Returns 0=OK, 1=failed. */
|
|
static uint8_t i2c_start_wait(uint8_t address)
|
|
{
|
|
uint32_t tickstart = HAL_GetTick();
|
|
bool wip;
|
|
do {
|
|
wip = i2c_start(address);
|
|
if (wip) i2c_stop();
|
|
} while (wip && (HAL_GetTick() - tickstart < EEPROM_TIMEOUT));
|
|
return wip;
|
|
}
|
|
|
|
|
|
bool eeprom_init(void)
|
|
{
|
|
/* enable BKUP SRAM */
|
|
HAL_PWR_EnableBkUpAccess();
|
|
__HAL_RCC_BKPSRAM_CLK_ENABLE();
|
|
|
|
/* check EEPROM connectivity */
|
|
i2c_init();
|
|
if (i2c_start_wait(EEPROM_ADDR+I2C_READ) != 0) return false;
|
|
i2c_stop();
|
|
|
|
/* log hardfault reason */
|
|
if (BKUP->magic == SYSLOG_MAGIC) {
|
|
eeprom_write(ADDR_HARDFAULT, BKUP, sizeof(SYSLOG_HARDFAULT));
|
|
BKUP->magic = 0;
|
|
syslog_event(LOG_HARDFAULT);
|
|
}
|
|
|
|
/* log reset source */
|
|
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) syslog_event(LOG_RST_IWDG);
|
|
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) syslog_event(LOG_RST_WWDG);
|
|
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) syslog_event(LOG_RST_BOR);
|
|
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) syslog_event(LOG_RST_SFTR);
|
|
else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) syslog_event(LOG_RST_PIN);
|
|
__HAL_RCC_CLEAR_RESET_FLAGS();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void eeprom_set_freq(uint32_t hclk)
|
|
{
|
|
/* calculate ticks count for half bit delay based on HCLK */
|
|
i2c_delay_value = (hclk / 5) / (EEPROM_SPEED * 2);
|
|
if (i2c_delay_value < 2) i2c_delay_value = 1;
|
|
else i2c_delay_value--;
|
|
}
|
|
|
|
|
|
bool eeprom_read(uint16_t addr_eeprom, void *addr_ram, uint16_t length)
|
|
{
|
|
uint8_t *buffer = (uint8_t *)(addr_ram);
|
|
|
|
if (i2c_start_wait(EEPROM_ADDR+I2C_WRITE) != 0) return false;
|
|
i2c_write(addr_eeprom >> 8);
|
|
i2c_write(addr_eeprom >> 0);
|
|
i2c_rep_start(EEPROM_ADDR+I2C_READ);
|
|
while (length > 1) {
|
|
*buffer++ = i2c_read(1); // ACK
|
|
length--;
|
|
}
|
|
*buffer = i2c_read(0); // NAK
|
|
i2c_stop();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool eeprom_write(uint16_t addr_eeprom, void *addr_ram, uint16_t length)
|
|
{
|
|
uint8_t *buffer = (uint8_t *)(addr_ram);
|
|
|
|
next_page:
|
|
if (i2c_start_wait(EEPROM_ADDR+I2C_WRITE) != 0) return false;
|
|
i2c_write(addr_eeprom >> 8);
|
|
i2c_write(addr_eeprom >> 0);
|
|
while (length > 0) {
|
|
i2c_write(*buffer++);
|
|
addr_eeprom++;
|
|
length--;
|
|
if ((addr_eeprom & 0x001F) == 0 && length > 0) {
|
|
/* page boundary reached - store the page and continue with next one */
|
|
i2c_stop();
|
|
HAL_IWDG_Refresh(&hiwdg);
|
|
goto next_page;
|
|
}
|
|
}
|
|
i2c_stop();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool eeprom_erase_full(bool erase_config)
|
|
{
|
|
uint16_t addr = erase_config ? ADDR_CONFIG : ADDR_EVENTS;
|
|
uint8_t buffer[128]; // min. 2 pages to reset watchdog
|
|
|
|
memset(buffer, 0xFF, sizeof(buffer));
|
|
if (!erase_config) eeprom_write(ADDR_HARDFAULT, buffer, 0x0020);
|
|
while (addr < 0x2000) {
|
|
if (!eeprom_write(addr, buffer, sizeof(buffer))) return false;
|
|
addr += sizeof(buffer);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void config_load_default(void)
|
|
{
|
|
memcpy(&config, &config_default, sizeof(config));
|
|
}
|
|
|
|
|
|
bool config_load_eeprom(void)
|
|
{
|
|
uint32_t crc;
|
|
uint8_t *c = (uint8_t*)&config;
|
|
|
|
if (!eeprom_read(ADDR_CONFIG, &config, sizeof(config))) {
|
|
config_load_default();
|
|
return false;
|
|
}
|
|
|
|
HAL_CRC_Calculate(&hcrc, NULL, 0); // reset CRC
|
|
for (uint16_t i = 0; i < sizeof(config) - sizeof(config.crc); i++) {
|
|
uint32_t data = c[i]; // use as 8 bits
|
|
crc = HAL_CRC_Accumulate(&hcrc, &data, 1);
|
|
}
|
|
|
|
if (crc != config.crc) {
|
|
config_load_default();
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
void config_save_eeprom(void)
|
|
{
|
|
uint32_t crc;
|
|
uint8_t *c = (uint8_t*)&config;
|
|
|
|
HAL_CRC_Calculate(&hcrc, NULL, 0); // reset CRC
|
|
for (uint16_t i = 0; i < sizeof(config) - sizeof(config.crc); i++) {
|
|
uint32_t data = c[i]; // use as 8 bits
|
|
config.crc = HAL_CRC_Accumulate(&hcrc, &data, 1);
|
|
}
|
|
|
|
// write only if CRC changed
|
|
eeprom_read(ADDR_CONFIG + sizeof(config) - sizeof(crc), &crc, sizeof(crc));
|
|
if (crc != config.crc) eeprom_write(ADDR_CONFIG, &config, sizeof(config));
|
|
}
|
|
|
|
|
|
void syslog_read_nvinfo(char *str)
|
|
{
|
|
char *end = str + SYSLOG_MAX_LENGTH - 8;
|
|
|
|
str += snprintf(str, end-str, "%s NVinfo at %u\rbuild " __DATE__ "\r", config.callsign, (unsigned int)HAL_GetTick());
|
|
if (str > end) return;
|
|
|
|
EVENT_COUNTER ec;
|
|
for (uint8_t i = 0; i < LOG_EVENT_LAST; i++) {
|
|
eeprom_read(ADDR_EVENTS + i*EEPROM_PAGESIZE, &ec, sizeof(ec));
|
|
if (ec.counter != 0xFFFFFFFF) {
|
|
str += snprintf(str, end-str, "%s %d at %u\r", counter_name[i], (int)ec.counter+1, (unsigned int)ec.last_tick);
|
|
if (str > end) return;
|
|
}
|
|
}
|
|
|
|
SYSLOG_HARDFAULT sh;
|
|
eeprom_read(ADDR_HARDFAULT, &sh, sizeof(sh));
|
|
if (sh.fault != 0xFFFFFFFF) {
|
|
str += snprintf(str, end-str, "hardfault tick %u, fault %#x at %#x, pc %#x, lr %#x\r",
|
|
(unsigned int)sh.tick, (unsigned int)sh.fault, (unsigned int)sh.addr_fault, (unsigned int)sh.addr_pc, (unsigned int)sh.addr_lr);
|
|
if (str > end) return;
|
|
}
|
|
|
|
extern void *_crc_end;
|
|
uint32_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t*)FLASH_BASE, ((uint32_t)(&_crc_end) - FLASH_BASE) / 4);
|
|
str += snprintf(str, end-str, "flash crc %#08x\r", (unsigned int)crc);
|
|
if (str > end) return;
|
|
|
|
if (config.user_pin == 0) {
|
|
str += snprintf(str, end-str, "master-pin %lu\r", auth_get_master_pin());
|
|
if (str > end) return;
|
|
}
|
|
}
|
|
|
|
|
|
void syslog_read_config(char *str)
|
|
{
|
|
char *end = str + SYSLOG_MAX_LENGTH - 8;
|
|
|
|
str += snprintf(str, end-str, "%s config at %u\r", config.callsign, (unsigned int)HAL_GetTick());
|
|
if (str > end) return;
|
|
|
|
str += snprintf(str, end-str, "ov2640 delay %u, qs %u, agc %u, aec %u, agc-ceiling %u, agc-manual %u, aec-manual %u, awb %u, rotate %u\r",
|
|
config.cam.delay, config.cam.qs, config.cam.agc, config.cam.aec, config.cam.agc_ceiling,
|
|
config.cam.agc_manual, config.cam.aec_manual, config.cam.awb, config.cam.rotate
|
|
);
|
|
if (str > end) return;
|
|
|
|
str += snprintf(str, end-str, "camera temp %d'C, voltage %umV, holdoff %u, autoreboot %lu\r",
|
|
adc_read_temperature(), adc_read_voltage(), config.holdoff, config.autoreboot
|
|
);
|
|
if (str > end) return;
|
|
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
str += snprintf(str, end-str, "mode %d '%s' on %d\r",
|
|
i, config.mode_cmd[i], config.start_edge
|
|
);
|
|
if (str > end) return;
|
|
}
|
|
}
|
|
|
|
|
|
void syslog_event(LOG_EVENT event)
|
|
{
|
|
EVENT_COUNTER ec;
|
|
uint16_t addr = ADDR_EVENTS + event*EEPROM_PAGESIZE;
|
|
|
|
/* test for valid event ID */
|
|
if (event >= LOG_EVENT_LAST) return;
|
|
|
|
/* read EEPROM counter, increment and save back */
|
|
ec.counter = syslog_get_counter(event) + 1;
|
|
ec.last_tick = HAL_GetTick();
|
|
eeprom_write(addr, &ec, sizeof(ec));
|
|
}
|
|
|
|
|
|
uint32_t syslog_get_counter(LOG_EVENT event)
|
|
{
|
|
EVENT_COUNTER ec;
|
|
if (!eeprom_read(ADDR_EVENTS + event*EEPROM_PAGESIZE, &ec, sizeof(ec))) ec.counter = 0;
|
|
|
|
return ec.counter;
|
|
}
|
|
|
|
|
|
uint32_t auth_get_master_pin(void)
|
|
{
|
|
#define UID_BASE 0x1FFF7A10UL /*!< Unique device ID register base address */
|
|
return ((*((uint32_t *)UID_BASE) ^ *((uint32_t *)UID_BASE+4) ^ *((uint32_t *)UID_BASE+8) ^ 0x4438f5a1) % 1000000) | 0x20000;
|
|
}
|
|
|
|
|
|
bool auth_check_token(uint32_t token)
|
|
{
|
|
if (token == config.user_pin || token == auth_get_master_pin()) {
|
|
auth_tick_end = HAL_GetTick() + AUTH_TIME*1000;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool auth_check_req(void)
|
|
{
|
|
if (HAL_GetTick() < auth_tick_end || config.user_pin == 0) return true;
|
|
auth_tick_end = 0;
|
|
return false;
|
|
}
|
|
|