/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
*
© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum {
Tube_A = 3,
Tube_B = 2,
Tube_D = 1,
Tube_E = 0
} tube_pos_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define SPI_BUFFER_SIZE 5
/* Display timeout, sec */
#define DISP_WDT_TIME 10
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile flag_t Flag = {0};
/**
* Nixi Tube cathodes map in Byte Array:
* {E0 E9 E8 E7 E6 E5 E4 E3}
* {E2 E1 D0 D9 D8 D7 D6 D5}
* {D4 D3 D2 D1 B0 B9 B8 B7}
* {B6 B5 B4 B3 B2 B1 A0 A9}
* {A8 A7 A6 A5 A4 A3 A2 A1}
*
* Shift register bit map in Tube cathodes (from 0 to 1):
* {5.7 5.6 5.5 5.4 5.3 5.2 5.1 5.0 4.7 4.6} VL5/E
* {4.5 4.4 4.3 4.2 4.1 4.0 3.7 3.6 3.5 3.4} VL4/D
* {3.3 3.2 3.1 3.0 2.7 2.6 2.5 2.4 2.3 2.2} VL2/B
* {2.1 2.0 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0} VL1/A
*/
static const uint16_t nixieCathodeMap[4][10] = {
{0x8000, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000},
{0x2000, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000},
{0x0800, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400},
{0x0200, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100}
};
//static const uint8_t nixieCathodeMask[4][2] = {{0x00, 0x3f}, {0xc0, 0x0f}, {0xf0, 0x03}, {0xc0, 0x00}};
static uint8_t tubesBuffer[SPI_BUFFER_SIZE] = {0};
static rtc_t Clock;
static struct bme280_dev SensorDev;
static struct bme280_data SensorData;
static int8_t Humidity, Temperature;
static nt16_t Pressure;
static btn_t Button[BTN_NUM] = {
{0, evBTN1Pressed, evBTN1Holded, BTN1_PIN},
{0, evBTN2Pressed, evBTN2Pressed, BTN2_PIN},
{0, evBTN3Pressed, evBTN3Pressed, BTN3_PIN},
{0, evBTN4Pressed, evBTN4Holded, BTN4_PIN}
};
static volatile uint8_t dispWDT = 0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void showDigits(uint8_t * dig);
static void sensor_Init(void);
static void sensorStartMeasure(void);
static void sensorGetData(void);
static void btnProcess(void);
static void Color_RGB(uint8_t r, uint8_t g, uint8_t b);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* Initialize onBoard Hardware */
Board_Init();
/* Initialize Scheduler */
RTOS_Init();
/* tdelay_ms() work now, I2C can work too */
RTC_Init();
RTC_ReadAll(&Clock);
sensor_Init();
/* Initialize Event State Machine */
ES_Init(stShowTime);
es_event_t event = eventNull;
/* Enable tube power */
TUBE_PWR_ON;
/** Star SPI transfer to shift registers */
/* Set DMA source and destination addresses. */
/* Source: Address of the SPI buffer. */
DMA1_Channel1->CMAR = (uint32_t)&tubesBuffer;
/* Destination: SPI1 data register. */
DMA1_Channel1->CPAR = (uint32_t)&(SPI1->DR);
/* Set DMA data transfer length (SPI buffer length). */
DMA1_Channel1->CNDTR = SPI_BUFFER_SIZE;
/* Enable SPI transfer */
SPI1->CR1 |= SPI_CR1_SPE;
Flag.SPI_TX_End = 1;
/* display work now */
/** Set tasks for Sheduler */
RTOS_SetTask(btnProcess, 1, BTN_SCAN_PERIOD);
/* USER CODE BEGIN WHILE */
Color_RGB(0xFF, 0x12, 0x0); // Nixie color. FF1200 or FF7E00 or FFBF00
showTime();
/* Infinite loop */
while (1)
{
/* new second interrupt from RTC */
if (Flag.RTC_IRQ != 0) {
Flag.RTC_IRQ = 0;
Blink_Start(); // !!! TODO
RTC_ReadAll(&Clock);
if (dispWDT != 0) {
dispWDT --;
if (dispWDT == 0) {
ES_PlaceEvent(evDisplayWDT);
}
}
} /* end of New second */
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
event = ES_GetEvent();
if (event) {
ES_Dispatch(event);
}
RTOS_DispatchTask();
__WFI();
}
/* USER CODE END 3 */
} /* End of mine() */
/**
* Sensor
*/
static void sensor_Init(void) {
int8_t rsltSensor;
Flag.BME280 = 0;
SensorDev.dev_id = (BME280_I2C_ADDR_PRIM << 1);
SensorDev.intf = BME280_I2C_INTF;
SensorDev.read = user_i2c_read;
SensorDev.write = user_i2c_write;
SensorDev.delay_ms = tdelay_ms;
rsltSensor = bme280_init(&SensorDev);
if (rsltSensor == BME280_OK) {
Flag.BME280 = 1;
/* BME280 Recommended mode of operation: Indoor navigation */
SensorDev.settings.osr_h = BME280_OVERSAMPLING_1X;
SensorDev.settings.osr_p = BME280_OVERSAMPLING_16X;
SensorDev.settings.osr_t = BME280_OVERSAMPLING_2X;
SensorDev.settings.filter = BME280_FILTER_COEFF_16;
rsltSensor = bme280_set_sensor_settings((BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL), &SensorDev);
RTOS_SetTask(sensorStartMeasure, 103, 1000);
RTOS_SetTask(sensorGetData, 603, 1000);
}
}
static void sensorStartMeasure(void) {
bme280_set_sensor_mode(BME280_FORCED_MODE, &SensorDev);
}
static void sensorGetData(void) {
bme280_get_sensor_data(BME280_ALL, &SensorData, &SensorDev);
int32_t tmp;
tmp = SensorData.humidity + 512;
Humidity = (int8_t)(tmp / 1024);
tmp = SensorData.temperature + 50;
Temperature = (int8_t)(tmp / 100);
/* in 32-bit arithmetics pressure in Pa */
tmp = SensorData.pressure * 1000;
tmp += 66661;
tmp /= 133322;
/* pressure in mmHg */
Pressure.s16.u8H = (uint8_t)(tmp / 100);
Pressure.s16.u8L = (uint8_t)(tmp % 100);
}
/* USER CODE BEGIN 4 */
/*************************
* S U B R O U T I N E S *
*************************/
/**
* @brief Out digits to SPI buffer. ON/off tube power.
* @param : array with four BCD digits
* @retval : None
*/
static void showDigits(uint8_t * dig)
{
/* Clear buffer */
tubesBuffer[0] = 0;
tubesBuffer[1] = 0;
tubesBuffer[2] = 0;
tubesBuffer[3] = 0;
tubesBuffer[4] = 0;
/* check values range */
int i;
for (i=0; i<4; i++) {
if (dig[i] > 9) {
if (dig[i] != 0xf) {
dig[i] = 0;
}
}
}
/* Wait for SPI */
while (Flag.SPI_TX_End == 0) {};
Flag.SPI_TX_End = 0;
/* Feel buffer */
tubesBuffer[0] = (uint8_t)(nixieCathodeMap[Tube_E][dig[Tube_E]] >> 8);
tubesBuffer[1] = (uint8_t)((nixieCathodeMap[Tube_E][dig[Tube_E]]) | (nixieCathodeMap[Tube_D][dig[Tube_D]] >> 8));
tubesBuffer[2] = (uint8_t)((nixieCathodeMap[Tube_D][dig[Tube_D]]) | (nixieCathodeMap[Tube_B][dig[Tube_B]] >> 8));
tubesBuffer[3] = (uint8_t)((nixieCathodeMap[Tube_B][dig[Tube_B]]) | (nixieCathodeMap[Tube_A][dig[Tube_A]] >> 8));
tubesBuffer[4] = (uint8_t)(nixieCathodeMap[Tube_A][dig[Tube_A]]);
/* Start DMA transfer to SPI */
DMA1_Channel1->CCR |= DMA_CCR_EN;
/* On/Off tube power */
if (dig[Tube_A] == 0xf) {
TUBE_A_OFF;
} else {
TUBE_A_ON;
}
if (dig[Tube_B] == 0xf) {
TUBE_B_OFF;
} else {
TUBE_B_ON;
}
if (dig[Tube_D] == 0xf) {
TUBE_D_OFF;
} else {
TUBE_D_ON;
}
if (dig[Tube_E] == 0xf) {
TUBE_E_OFF;
} else {
TUBE_E_ON;
}
}
/**
* @brief Вывод HEX значений цвета в таймер.
* @param : RGB value in range 0x00-0xFF
* @retval : None
*/
static void Color_RGB(uint8_t r, uint8_t g, uint8_t b) {
/* Более быстрый вариант, на пробу. */
COLOR_R(r * 4);
COLOR_G(g * 4);
COLOR_B(b * 4);
/* Предварительный обсчёт в переменные сделан для того,
что-бы вывести значения в таймер максимально одновременно. */
/*
uint32_t val_r, val_g, val_b;
// * 999 + 127 / 255 ???
val_r = ((uint32_t)(r * 1000) + 128) / 256;
val_g = ((uint32_t)(g * 1000) + 128) / 256;
val_b = ((uint32_t)(b * 1000) + 128) / 256;
COLOR_R((uint16_t)val_r);
COLOR_G((uint16_t)val_g);
COLOR_B((uint16_t)val_b);
*/
}
/**
* @brief Обработка кнопок.
* @param : None
* @retval : None
*/
static void btnProcess(void) {
/* get pin state */
uint32_t pins = BTNS_STATE;
int i;
for (i=0; i= (BTN_TIME_HOLDED/BTN_SCAN_PERIOD)) {
Button[i].time -= (BTN_TIME_REPEATED/BTN_SCAN_PERIOD);
if (Button[i].holded == Button[i].pressed) {
/* if pressed and holded - same function, then button pressed auto repeat */
ES_PlaceEvent(Button[i].pressed);
}
}
} else if (Button[i].time != 0) {
/* button released */
if (Button[i].time >= ((BTN_TIME_HOLDED - BTN_TIME_REPEATED)/BTN_SCAN_PERIOD)) {
/* process long press */
ES_PlaceEvent(Button[i].holded);
} else if (Button[i].time >= (BTN_TIME_PRESSED/BTN_SCAN_PERIOD)) {
/* process short press */
ES_PlaceEvent(Button[i].pressed);
}
Button[i].time = 0;
RTOS_SetTask(btnProcess, BTN_SCAN_PAUSE, BTN_SCAN_PERIOD);
}
} /* end FOR */
}
/**
* On/off symbols on IN-15 tube.
*/
void in15Off(void) {
IN15_OFF;
TUBE_C_OFF;
}
void in15Minus(void) {
IN15_OFF;
IN15_Minus;
TUBE_C_ON;
}
void in15Plus(void) {
IN15_OFF;
IN15_Plus;
TUBE_C_ON;
}
void in15Percent(void) {
IN15_OFF;
IN15_Percent;
TUBE_C_ON;
}
void in15P(void) {
IN15_OFF;
IN15_P;
TUBE_C_ON;
}
void showTime(void) {
in15Minus();
RTOS_SetTask(in15Off, 500, 0);
uint8_t buf[4];
buf[Tube_A] = Clock.Hr >> 4;
buf[Tube_B] = Clock.Hr & 0xf;
buf[Tube_D] = Clock.Min >> 4;
buf[Tube_E] = Clock.Min & 0xf;
showDigits(buf);
}
/**
* Show info on tubes.
*/
void showWD(void) {
dispWDT = DISP_WDT_TIME;
IN15_OFF;
uint8_t buf[4];
buf[Tube_A] = 0xf;
buf[Tube_B] = Clock.WD & 0xf;
buf[Tube_D] = 0xf;
buf[Tube_E] = 0xf;
showDigits(buf);
}
void showDay(void) {
dispWDT = DISP_WDT_TIME;
IN15_OFF;
uint8_t buf[4];
buf[Tube_A] = Clock.Day >> 4;
buf[Tube_B] = Clock.Day & 0xf;
buf[Tube_D] = 0xf;
buf[Tube_E] = 0xf;
showDigits(buf);
}
void showMonth(void) {
dispWDT = DISP_WDT_TIME;
IN15_OFF;
uint8_t buf[4];
buf[Tube_A] = 0xf;
buf[Tube_B] = 0xf;
buf[Tube_D] = Clock.Mon >> 4;
buf[Tube_E] = Clock.Mon & 0xf;
showDigits(buf);
}
void showDayMon(void) {
dispWDT = DISP_WDT_TIME;
IN15_OFF;
uint8_t buf[4];
buf[Tube_A] = Clock.Day >> 4;
buf[Tube_B] = Clock.Day & 0xf;
buf[Tube_D] = Clock.Mon >> 4;
buf[Tube_E] = Clock.Mon & 0xf;
showDigits(buf);
}
void showYear(void) {
dispWDT = DISP_WDT_TIME;
IN15_OFF;
uint8_t buf[4];
buf[Tube_A] = 2;
buf[Tube_B] = 0;
buf[Tube_D] = Clock.Year >> 4;
buf[Tube_E] = Clock.Year & 0xf;
showDigits(buf);
}
void showHumidity(void) {
dispWDT = DISP_WDT_TIME;
in15Percent();
uint8_t buf[4];
buf[Tube_A] = Humidity / 10;
buf[Tube_B] = Humidity % 10;
buf[Tube_D] = 0xf;
buf[Tube_E] = 0xf;
showDigits(buf);
}
void showTemperature(void) {
dispWDT = DISP_WDT_TIME;
in15Plus();
uint8_t buf[4];
buf[Tube_A] = 0xf;
buf[Tube_B] = 0xf;
buf[Tube_D] = Temperature / 10;
buf[Tube_E] = Temperature % 10;
showDigits(buf);
}
void showPressure(void) {
dispWDT = DISP_WDT_TIME;
in15P();
uint8_t buf[4];
buf[Tube_A] = 0xf;
buf[Tube_B] = Pressure.s16.u8H & 0xf;
buf[Tube_D] = Pressure.s16.u8L >> 4;
buf[Tube_E] = Pressure.s16.u8L & 0xf;
showDigits(buf);
}
/* Simple function for cyclic show all sensor data */
void showSensorData(void) {
ES_SetState(stShowSensorData);
showTemperature();
tdelay_ms(3000);
showHumidity();
tdelay_ms(3000);
showPressure();
tdelay_ms(3000);
ES_SetState(stShowTime);
showTime();
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/