minor cleanup and moves prior to adding USB support

This commit is contained in:
true
2024-11-06 19:15:40 -08:00
parent 50ab3fd036
commit a903464666
17 changed files with 96 additions and 51 deletions

View File

@@ -0,0 +1,183 @@
/*
* lightsense.c
*
* Created on: Oct 18, 2024
* Author: true
*/
#include <ch32v20x.h>
#include <stdint.h>
#include "periph/adc.h"
static GPIO_InitTypeDef lsens_gpio = {
.GPIO_Mode = GPIO_Mode_Out_PP,
.GPIO_Pin = LSENS_A_PIN,
.GPIO_Speed = GPIO_Speed_2MHz
};
uint16_t lsens_val;
static uint8_t lsens_mode = LSENS_READING_IDLE;
uint8_t lsens_wait;
uint8_t lsens_coarse = 1;
uint16_t lsens_timeout;
void adc_init()
{
ADC_InitTypeDef adc = {0};
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
ADC_DeInit(ADC1);
adc.ADC_Mode = ADC_Mode_Independent;
adc.ADC_ScanConvMode = DISABLE;
adc.ADC_ContinuousConvMode = DISABLE;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &adc);
ADC_RegularChannelConfig(ADC1, LSENS_ADC_CH, 1, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);
}
void adc_convert()
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
void adc_read()
{
uint16_t timeout = 0xfff;
while((!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) && timeout) timeout--;
if (timeout) {
lsens_val = ADC_GetConversionValue(ADC1);
}
}
void adc_set_mode_lsens(uint8_t mode)
{
lsens_mode = mode;
if (mode == LSENS_OUTPUT) {
lsens_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
lsens_gpio.GPIO_Pin = LSENS_A_PIN;
GPIO_Init(LSENS_A_PORT, &lsens_gpio);
lsens_gpio.GPIO_Pin = LSENS_K_PIN;
GPIO_Init(LSENS_K_PORT, &lsens_gpio);
}
}
uint8_t adc_get_mode_lsens()
{
return lsens_mode;
}
static void lsens_start()
{
// set anode and cathode low
LSENS_A_PORT->BCR = LSENS_A_PIN;
LSENS_K_PORT->BCR = LSENS_K_PIN;
adc_set_mode_lsens(LSENS_READING_START);
// set cathode high, let it charge
LSENS_K_PORT->BSHR = LSENS_K_PIN;
// set anode low
lsens_gpio.GPIO_Pin = LSENS_A_PIN;
lsens_gpio.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(LSENS_A_PORT, &lsens_gpio);
// set cathode as analog input
lsens_gpio.GPIO_Pin = LSENS_K_PIN;
lsens_gpio.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(LSENS_K_PORT, &lsens_gpio);
}
static void lsens_stop()
{
lsens_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
lsens_gpio.GPIO_Pin = LSENS_A_PIN;
GPIO_Init(LSENS_A_PORT, &lsens_gpio);
lsens_gpio.GPIO_Pin = LSENS_K_PIN;
GPIO_Init(LSENS_K_PORT, &lsens_gpio);
lsens_mode = LSENS_READING_IDLE;
}
void adc_process_lsens()
{
if (lsens_mode != LSENS_OUTPUT) {
// do what needs to be done by me to defeat the light enemys
switch (lsens_mode) {
case LSENS_READING_IDLE: {
// prepare LED, wait a little bit
lsens_start();
adc_convert();
lsens_wait = lsens_coarse;
lsens_mode = LSENS_READING_START;
break;
}
case LSENS_READING_START: {
if (!lsens_wait) {
// convert the LED
adc_convert();
lsens_mode = LSENS_READING_WAIT;
}
lsens_wait--;
break;
}
case LSENS_READING_WAIT: {
// read the light sensor value
adc_read();
lsens_stop();
// calculate adjustments
if (lsens_val > LSENS_COARSE_UP) {
lsens_coarse++;
if (lsens_coarse > 0x3f) lsens_coarse = 0x3f;
} else if (lsens_val < LSENS_COARSE_DOWN) {
if (lsens_coarse) lsens_coarse--;
}
lsens_wait = 255 - lsens_coarse;
// wait a bit before doing it again
lsens_mode = LSENS_READING_TIMEOUT;
break;
}
case LSENS_READING_TIMEOUT: {
if (!lsens_wait) {
// do it all again
lsens_mode = LSENS_READING_IDLE;
}
lsens_wait--;
}
}
}
}
uint16_t adc_get_lsens()
{
return lsens_val;
}
uint8_t adc_get_lsens_coarse()
{
return lsens_coarse;
}

View File

@@ -0,0 +1,52 @@
/*
* lightsense.h
*
* Created on: Oct 18, 2024
* Author: true
*/
#ifndef USER_PERIPH_ADC_H_
#define USER_PERIPH_ADC_H_
#define LSENS_DARK_THRESHOLD 0x7ff // baseline minimum value reading achieved in darkness
#define LSENS_A_PORT GPIOA
#define LSENS_A_PIN GPIO_Pin_5
#define LSENS_K_PORT GPIOB
#define LSENS_K_PIN GPIO_Pin_0
#define LSENS_ADC_CH ADC_Channel_8
#define LSENS_COARSE_UP 0x990 // counts higher than this increase lsens_coarse, maximum 64
#define LSENS_COARSE_DOWN 0x890 // counts lower than this decrease lsens_coarse, minimum 1
#define LSENS_DARK 48
#define LSENS_BRIGHT 32
enum lsens_mode {
LSENS_READING_IDLE = 0,
LSENS_READING_START,
LSENS_READING_WAIT,
LSENS_READING_TIMEOUT,
LSENS_OUTPUT = 0xff
};
void adc_init();
void adc_convert();
void adc_read();
void adc_set_mode_lsens(uint8_t mode);
uint8_t adc_get_mode_lsens();
void adc_process_lsens();
uint16_t adc_get_lsens();
uint8_t adc_get_lsens_coarse();
#endif /* USER_PERIPH_ADC_H_ */

View File

@@ -0,0 +1,102 @@
/*
* btn.c
*
* handles buttons as well as the
* DIP switches (in case action is desired by hot-switching)
*/
#include <ch32v20x.h>
#include "periph/btn.h"
struct Btn btn[BTN_COUNT] = {0};
void btn_init()
{
uint8_t i;
// this function assumes GPIO has been configured already
// initialize default setup
btn[BTN1]._pintype = BTN1_PIN;
btn[BTN2]._pintype = BTN2_PIN;
btn[DIP1]._pintype = DIP1_PIN;
btn[DIP2]._pintype = DIP2_PIN;
btn[DIP3]._pintype = DIP3_PIN;
for (i = 0; i < BTN_COUNT; i++) {
btn[i]._mask = BTN_RELEASE;
// ignore any currently pressed buttons
if (!(BTN_PORT->INDR & (1 << (btn[i]._pintype & BTN_PIN_MASK)))) {
btn[0]._mask |= BTN_IGNORE;
}
}
}
void btn_poll()
{
uint8_t i;
uint8_t ignore;
uint8_t pushed;
for (i = 0; i < BTN_COUNT; i++) {
ignore = btn[i]._mask & BTN_IGNORE;
// active low type buttons
pushed = BTN_PORT->INDR & (1 << (btn[i]._pintype & BTN_PIN_MASK)) ? 0 : 1;
if (pushed) {
// hold counter
if (btn[i]._count < 0xffff) btn[i]._count++;
// pushed long enough?
if (btn[i]._count < BTN_DEBOUNCE) continue;
// first push?
if (!(btn[i]._mask & BTN_PUSH)) {
btn[i]._mask = BTN_PUSH | ignore;
if (btn[i].cb_push && !ignore) {
btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4);
}
} else if (btn[i]._count >= btn[i].hold) {
// held to count limit
// if button is not repeatable, do not retrigger
if ((btn[i]._mask & BTN_HOLD) && !btn[i].repeat) continue;
btn[i]._mask |= BTN_HOLD;
// call callback only if not in ignore state
if (btn[i].cb_hold && !ignore) {
btn[i].cb_hold(i);
btn[i]._mask |= (BTN_HOLD << 4);
}
// apply repeat rate to count
if (btn[i].repeat > btn[i]._count) {
btn[i]._count = 0;
} else btn[i]._count -= btn[i].repeat;
}
} else {
// is not pushed
if (!(btn[i]._mask & BTN_RELEASE)) {
// note: release will remove ignore status
btn[i]._mask = BTN_RELEASE;
btn[i]._count = 0;
// call callback only if not in ignore state
if (btn[i].cb_release && !ignore) {
btn[i].cb_release(i);
btn[i]._mask |= (BTN_RELEASE << 4);
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* btn.h
*/
#ifndef USER_PERIPH_BTN_H_
#define USER_PERIPH_BTN_H_
#include <ch32v20x.h>
#define BTN_COUNT 5
#define BTN_DEBOUNCE (24 / 4) // debounce time in ~4ms increments
#define BTN_PORT GPIOB
#define BTN1 0
#define BTN1_PIN 10
#define BTN2 1
#define BTN2_PIN 11
#define DIP1 2
#define DIP1_PIN 12
#define DIP2 3
#define DIP2_PIN 13
#define DIP3 4
#define DIP3_PIN 14
#define BTN_PIN_MASK 0xf
#define BTN_PUSH (1 << 0)
#define BTN_HOLD (1 << 1)
#define BTN_RELEASE (1 << 2)
#define BTN_IGNORE (1 << 3)
typedef struct Btn {
uint8_t _pintype;
uint8_t _mask;
uint16_t _count; // held counts
uint16_t hold; // initial hold
uint16_t repeat; // repeated hold
void (*cb_push)(uint8_t);
void (*cb_hold)(uint8_t);
void (*cb_release)(uint8_t);
} Btn;
extern struct Btn btn[BTN_COUNT];
void btn_init();
void btn_poll();
#endif /* USER_PERIPH_BTN_H_ */

View File

@@ -0,0 +1,24 @@
/*
* gat_gpio.c
*
* Created on: Oct 16, 2024
* Author: true
*/
#include "periph/gat_gpio.h"
static GPIO_InitTypeDef gpio;
void gat_gpio_init()
{
// set the ID to 0 on GP pins.
gpio.GPIO_Speed = GPIO_Speed_2MHz;
gpio.GPIO_Mode = GPIO_Mode_IPD;
gpio.GPIO_Pin = (1 << GAT_GP1_PIN) | (1 << GAT_GP2_PIN);
GPIO_Init(GAT_GPIO_PORT, &gpio);
}

View File

@@ -0,0 +1,28 @@
/*
* gat_gpio.h
*
* Created on: Oct 16, 2024
* Author: true
*/
#ifndef USER_PERIPH_GAT_GPIO_H_
#define USER_PERIPH_GAT_GPIO_H_
#include <ch32v20x.h>
#define GAT_GPIO_PORT GPIOA
#define GAT_GP1_PIN 9
#define GAT_GP2_PIN 10
void gat_gpio_init();
#endif /* USER_PERIPH_GAT_GPIO_H_ */

View File

@@ -0,0 +1,120 @@
/*
* port_pwr.c
*
* Created Oct 16, 2024
*
* power control for GAT and USB ports.
*/
#include <ch32v20x.h>
#include "periph/port_pwr.h"
#define PORT_PWR_STATE_DR BKP_DR3
#define GAT_ON_FLAG (1 << 0)
#define USB2_ON_FLAG (1 << 1)
#define INIT_FLAG (0xaf << 8)
static uint16_t port_pwr_state;
static uint8_t gat_oc_state_latch = 0;
void port_pwr_state_commit_bkp()
{
BKP_WriteBackupRegister(PORT_PWR_STATE_DR, port_pwr_state);
RTC_WaitForLastTask();
}
void gat_on()
{
if (gat_oc_state_latch) return;
GAT_EN_PORT->BSHR = GAT_EN_PIN;
port_pwr_state |= GAT_ON_FLAG;
port_pwr_state_commit_bkp();
}
void gat_off()
{
GAT_EN_PORT->BCR = GAT_EN_PIN;
port_pwr_state &= ~GAT_ON_FLAG;
port_pwr_state_commit_bkp();
}
uint8_t gat_pwr_state()
{
return (GAT_EN_PORT->OUTDR & GAT_EN_PIN) ? 1 : 0;
}
uint8_t gat_oc_state()
{
if (!(GAT_OC_PORT->INDR & GAT_OC_PIN)) {
gat_oc_state_latch = 1;
gat_off();
}
return gat_oc_state_latch;
}
void gat_toggle()
{
if (gat_pwr_state()) gat_off();
else gat_on();
}
void usb2_on()
{
USB2_EN_PORT->BSHR = USB2_EN_PIN;
port_pwr_state |= USB2_ON_FLAG;
port_pwr_state_commit_bkp();
}
void usb2_off()
{
USB2_EN_PORT->BCR = USB2_EN_PIN;
port_pwr_state &= ~USB2_ON_FLAG;
port_pwr_state_commit_bkp();
}
uint8_t usb2_pwr_state()
{
return (USB2_EN_PORT->OUTDR & USB2_EN_PIN) ? 1 : 0;
}
void usb2_toggle()
{
if (usb2_pwr_state()) usb2_off();
else usb2_on();
}
void port_pwr_init()
{
port_pwr_state = BKP_ReadBackupRegister(PORT_PWR_STATE_DR);
RTC_WaitForLastTask();
if ((port_pwr_state & INIT_FLAG) != INIT_FLAG) {
// no battery retention.
// automatically turn on all outputs
port_pwr_state = INIT_FLAG | USB2_ON_FLAG | GAT_ON_FLAG;
// and commit state flags
port_pwr_state_commit_bkp();
}
if (port_pwr_state & GAT_ON_FLAG) gat_on();
if (port_pwr_state & USB2_ON_FLAG) usb2_on();
}

View File

@@ -0,0 +1,46 @@
/*
* port_pwr.h
*
* Created on: Oct 16, 2024
* Author: true
*/
#ifndef USER_PERIPH_PORT_PWR_H_
#define USER_PERIPH_PORT_PWR_H_
#include <ch32v20x.h>
// GAT port enable pin
#define GAT_EN_PORT GPIOA
#define GAT_EN_PIN GPIO_Pin_3
// overcurrent detect pin, active low
#define GAT_OC_PORT GPIOA
#define GAT_OC_PIN GPIO_Pin_4
#define USB2_EN_PORT GPIOB
#define USB2_EN_PIN GPIO_Pin_4
void port_pwr_init();
uint8_t gat_oc_state();
void gat_on();
void gat_off();
void gat_toggle();
uint8_t gat_pwr_state();
void usb2_on();
void usb2_off();
void usb2_toggle();
uint8_t usb2_pwr_state();
#endif /* USER_PERIPH_PORT_PWR_H_ */

View File

@@ -0,0 +1,151 @@
/*
* rgbled.c
*
* using TIM2 CH1-CH3
*
* * errata:
* - board design makes overcurrent on GAT port practically impossible.
* overcurrent condition may either trip low voltage condition due to
* decoupling or voltage output limit on LDO. as such, displaying
* any overcurrent state is meaningless.
*/
#include <ch32v20x.h>
#include <stdint.h>
#include "periph/btn.h"
#include "periph/port_pwr.h"
#include "periph/rtc.h"
#define RED 0
#define GRN 1
#define BLU 2
#define BRT_RED 40
#define BRT_GRN 32
#define BRT_BLU 36
#define BRT_OFF 0
#define RGBLED_TIM TIM2
static uint8_t flash_timeout[3];
static uint8_t flash[3] = {0};
static uint8_t state[3] = {0};
void rgbled_set()
{
int8_t i;
uint16_t out[3];
// flash counters
for (i = 0; i < 3; i++) {
if (!flash[i]) {
flash[i] = flash_timeout[i];
} else flash[i]--;
switch (flash_timeout[i]) {
// always on
case 0x00: {
state[i] = 1;
break;
}
// always off
case 0xff: {
state[i] = 0;
break;
}
// standard
default: {
if (flash[i] == flash_timeout[i]) {
state[i] ^= 1;
}
}
}
}
out[BLU] = BRT_BLU;
out[GRN] = BRT_GRN;
out[RED] = BRT_RED;
for (i = 0; i < 3; i++) {
// jumper 1 will reduce brightness
if (btn[DIP1]._mask & BTN_PUSH) {
out[i] >>= 2;
}
// jumper 2 will turn off LEDs entirely
if (btn[DIP2]._mask & BTN_PUSH) {
out[i] = BRT_OFF;
}
}
RGBLED_TIM->CH1CVR = state[BLU] ? out[BLU] : BRT_OFF;
RGBLED_TIM->CH2CVR = state[GRN] ? out[GRN] : BRT_OFF;
RGBLED_TIM->CH3CVR = state[RED] ? out[RED] : BRT_OFF;
}
void rgbled_update()
{
// VCR flash if clock isn't set
/*
switch (rtc_state) {
case RTC_STATE_CLOCK_NOT_SET: {
flash_timeout[BLU] = 48;
break;
}
default: {
flash_timeout[BLU] = 0;
break;
}
}
*/
// see errata on why we aren't showing overcurrent status anymore
// flash_timeout[RED] = gat_oc_state() ? 16 : 0xff;
// power output state
flash_timeout[GRN] = gat_pwr_state() ? 0 : 0xff;
flash_timeout[RED] = usb2_pwr_state() ? 0 : 0xff;
rgbled_set();
}
void rgbled_init()
{
TIM_TimeBaseInitTypeDef timer ={0};
TIM_OCInitTypeDef pwm = {0};
timer.TIM_Period = (1 << 10) - 1; // 10-bit
timer.TIM_Prescaler = 0;
timer.TIM_ClockDivision = TIM_CKD_DIV1;
timer.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(RGBLED_TIM, &timer);
pwm.TIM_OCMode = TIM_OCMode_PWM1;
pwm.TIM_OutputState = TIM_OutputState_Enable;
pwm.TIM_Pulse = BRT_OFF;
pwm.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1PreloadConfig(RGBLED_TIM, TIM_OCPreload_Disable);
TIM_OC2PreloadConfig(RGBLED_TIM, TIM_OCPreload_Disable);
TIM_OC3PreloadConfig(RGBLED_TIM, TIM_OCPreload_Disable);
TIM_ARRPreloadConfig(RGBLED_TIM, ENABLE);
TIM_OC1Init(RGBLED_TIM, &pwm);
TIM_OC2Init(RGBLED_TIM, &pwm);
TIM_OC3Init(RGBLED_TIM, &pwm);
TIM_CtrlPWMOutputs(RGBLED_TIM, ENABLE);
RGBLED_TIM->CNT = 0;
TIM_Cmd(RGBLED_TIM, ENABLE);
flash_timeout[BLU] = flash_timeout[GRN] = flash_timeout[RED] = 0xff;
}

View File

@@ -0,0 +1,25 @@
/*
* rgbled.h
*
* Created on: Oct 16, 2024
* Author: true
*/
#ifndef USER_PERIPH_RGBLED_H_
#define USER_PERIPH_RGBLED_H_
#define RGBLED_PORT GPIOA
#define RGBLED_PIN_B GPIO_Pin_0
#define RGBLED_PIN_G GPIO_Pin_1
#define RGBLED_PIN_R GPIO_Pin_2
void rgbled_init();
void rgbled_update();
#endif /* USER_PERIPH_RGBLED_H_ */

View File

@@ -0,0 +1,164 @@
/*
* rtc.c
*
* Created on: Oct 16, 2024
* Author: true
*/
#include <ch32v20x.h>
#include <stdint.h>
#include "periph/rtc.h"
#define RTC_INIT_PATTERN 0x1337
static const uint8_t days_per_mon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint8_t rtc_state;
void rtc_set_state(uint16_t state)
{
rtc_state = state;
}
/*
* @fn rtc_is_leapyear
* @brief Returns true if year is a leap year.
*
* @param year
*
* @return 1 - Yes
* 0 - No
*/
uint8_t rtc_is_leapyear(u16 year)
{
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
return 1;
} else {
return 0;
}
}
else return 1;
}
else return 0;
}
/*
* @fn rtc_set_clock
* @brief Set Time.
*
* @param Struct of RTClock
*
* @return 1 - error
* 0 - success
*/
int8_t rtc_set_clock(struct RTClock *c)
{
uint16_t x;
uint8_t m;
uint32_t count = 0;
if(c->year < 1970 || c->year > 2099)
return -1;
for(x = 1970; x < c->year; x++) {
if (rtc_is_leapyear(x))
count += 31622400;
else
count += 31536000;
}
m = c->mon - 1;
for(x = 0; x < m; x++){
count += (u32)days_per_mon[x] * 86400;
if(rtc_is_leapyear(c->year) && x == 1)
count += 86400;
}
count += (u32)(c->day - 1) * 86400;
count += (u32)c->h * 3600;
count += (u32)c->m * 60;
count += c->s;
PWR_BackupAccessCmd(ENABLE);
RTC_SetCounter(count);
RTC_WaitForLastTask();
return 0;
}
int8_t rtc_init()
{
uint32_t timeout;
static const struct RTClock clock = {
.year = 2024,
.mon = 11,
.day = 1,
.h = 8,
.m = 0,
.s = 0
};
// enable access to RTC registers
PWR_BackupAccessCmd(ENABLE);
// get RTC state
// if things aren't configured it'll be initialized in a moment
rtc_state = BKP_ReadBackupRegister(RTC_STATE_DR);
RTC_WaitForLastTask();
// is RTC already configured?
if (BKP_ReadBackupRegister(RTC_INIT_DR) != RTC_INIT_PATTERN) {
// must not be. initialize rtc
BKP_DeInit();
RCC_LSEConfig(RCC_LSE_ON);
timeout = SystemCoreClock / 2;
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && timeout--);
// if crystal didn't start in time, let the application know
if (!timeout) return -1;
// use crystal for RTC and enable it
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
// RTC_ITConfig(RTC_IT_ALR, ENABLE);
// RTC_ITConfig(RTC_IT_SEC, ENABLE);
// RTC_WaitForLastTask();
RTC_EnterConfigMode();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
// set an initial time
rtc_set_clock((struct RTClock *)&clock);
RTC_ExitConfigMode();
rtc_state = RTC_STATE_CLOCK_NOT_SET;
BKP_WriteBackupRegister(RTC_STATE_DR, rtc_state);
RTC_WaitForLastTask();
BKP_WriteBackupRegister(RTC_INIT_DR, RTC_INIT_PATTERN);
// RTC_NVIC_Config();
// RTC_Get();
}
RTC_WaitForLastTask();
return 0;
}

View File

@@ -0,0 +1,56 @@
/*
* rtc.h
*
* Created on: Oct 16, 2024
* Author: true
*/
#ifndef USER_PERIPH_RTC_H_
#define USER_PERIPH_RTC_H_
#include <stdint.h>
/* a small note about backup registers:
*
* the datasheet clearly states there are 42 16-bit registers.
* it does not cut out any exception for CH32V203 except CH32V203RB.
* however, the reference manual states that registers 11-42 are only
* for the _D8 chips, and not the _D6 V4B chips like this one.
* thus we only have _10_ backup registers, not 42.
*/
#define RTC_INIT_DR BKP_DR1
#define RTC_STATE_DR BKP_DR2
enum RTCState {
RTC_STATE_UNINITIALIZED = 0,
RTC_STATE_CLOCK_NOT_SET,
RTC_STATE_OK = 0x7f
};
typedef struct RTClock {
uint16_t year;
uint8_t mon;
uint8_t day;
uint8_t h;
uint8_t m;
uint8_t s;
} RTClock;
extern uint8_t rtc_state;
int8_t rtc_init();
int8_t rtc_set_clock(struct RTClock *c);
#endif /* USER_PERIPH_RTC_H_ */