--- /dev/null
+/*
+ * Air Conditioning Controller
+ *
+ * (c) 2019 Martin Mareš <mj@ucw.cz>
+ */
+
+#include "util.h"
+#include "ds18b20.h"
+
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/timer.h>
+#include <libopencm3/stm32/usart.h>
+
+static void clock_setup(void)
+{
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOB);
+ rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_USART1);
+ rcc_periph_clock_enable(RCC_USART3);
+ rcc_periph_clock_enable(RCC_TIM1);
+ rcc_periph_clock_enable(RCC_TIM2);
+ rcc_periph_clock_enable(RCC_TIM3);
+ rcc_periph_clock_enable(RCC_TIM4);
+ rcc_periph_clock_enable(RCC_DMA1);
+
+ rcc_periph_reset_pulse(RST_GPIOA);
+ rcc_periph_reset_pulse(RST_GPIOB);
+ rcc_periph_reset_pulse(RST_GPIOC);
+ rcc_periph_reset_pulse(RST_USART1);
+ rcc_periph_reset_pulse(RST_USART3);
+ rcc_periph_reset_pulse(RST_TIM1);
+ rcc_periph_reset_pulse(RST_TIM2);
+ rcc_periph_reset_pulse(RST_TIM3);
+ rcc_periph_reset_pulse(RST_TIM4);
+}
+
+static void gpio_setup(void)
+{
+ // PA6 = RS485 TX enable
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO6);
+ gpio_clear(GPIOA, GPIO6);
+
+ // PA7 = TIM3_CH2 for DS18B20 bus -- configured by ds18b20 library
+
+ // PA9 = TXD1 for debugging console
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
+
+ // PA10 = RXD1 for debugging console
+ gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
+
+ // PB0 = bypass LED*
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO0);
+ gpio_set(GPIOB, GPIO0);
+
+ // PB1 = IR RC active LED*
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
+ gpio_set(GPIOB, GPIO1);
+
+ // PB6 = TIM3_CH1 fan control opto-coupler
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO7);
+
+ // PB7 = TIM3_CH2 IR LED
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO7);
+
+ // PB10 = TXD3 for RS485
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO10);
+
+ // PB11 = RXD3 for RS485
+ gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11);
+
+ // PC13 = BluePill LED
+ gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
+ gpio_clear(GPIOC, GPIO13);
+
+ // PC15 = bypass opto-coupler
+ gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO15);
+ gpio_clear(GPIOC, GPIO15);
+}
+
+static volatile u32 ms_ticks;
+
+void sys_tick_handler(void)
+{
+ ms_ticks++;
+}
+
+static void tick_setup(void)
+{
+ systick_set_frequency(1000, 72000000);
+ systick_counter_enable();
+ systick_interrupt_enable();
+}
+
+static void delay_ms(uint ms)
+{
+ u32 start_ticks = ms_ticks;
+ while (ms_ticks - start_ticks < ms)
+ ;
+}
+
+static void usart_setup(void)
+{
+ usart_set_baudrate(USART1, 115200);
+ usart_set_databits(USART1, 8);
+ usart_set_stopbits(USART1, USART_STOPBITS_1);
+ usart_set_mode(USART1, USART_MODE_TX_RX);
+ usart_set_parity(USART1, USART_PARITY_NONE);
+ usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+
+ usart_enable(USART1);
+}
+
+#if 0
+
+static bool bypass_active;
+static byte pwm;
+
+static void show_temperature(void)
+{
+ debug_putc('#');
+ for (uint i=0; ds_sensors[i].address[0]; i++) {
+ debug_putc(' ');
+ int t = ds_sensors[i].current_temp;
+ if (t == DS_TEMP_UNKNOWN)
+ debug_puts("---.---");
+ else
+ debug_printf("%3d.%03d", t / 1000, t % 1000);
+ }
+ debug_printf(" %d", bypass_active);
+ debug_printf(" %d", pwm);
+ debug_puts("\r\n");
+}
+
+static void pwm_init(void)
+{
+ timer_set_prescaler(TIM4, 3); // clock = 72 MHz / 2 = 36 MHz
+ timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+ timer_disable_preload(TIM4);
+ timer_set_period(TIM4, 255); // PWM frequency = 18 MHz / 256 = 70.3125 kHz FIXME
+ timer_set_oc_mode(TIM4, TIM_OC1, TIM_OCM_PWM1);
+ timer_set_oc_value(TIM4, TIM_OC1, 1);
+ pwm = 1;
+ /*
+ * 1 0.03
+ * 4 0.48
+ * 8 1.54
+ * 12 2.71
+ * 16 3.99
+ * 24 6.34
+ * 32 7.85
+ * 64 7.95
+ * 128 8.02
+ * 255
+ */
+ timer_set_oc_polarity_high(TIM4, TIM_OC1);
+ timer_enable_counter(TIM4);
+ timer_enable_oc_output(TIM4, TIM_OC1);
+
+ gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO6);
+}
+
+#endif
+
+int main(void)
+{
+ clock_setup();
+ gpio_setup();
+ tick_setup();
+ usart_setup();
+ // pwm_init();
+
+ // ds_init();
+
+ debug_puts("Hello, world!\n");
+
+ for (;;) {
+ gpio_toggle(GPIOC, GPIO13);
+
+ gpio_clear(GPIOB, GPIO1);
+ delay_ms(100);
+ gpio_set(GPIOB, GPIO1);
+
+ gpio_clear(GPIOB, GPIO0);
+ delay_ms(100);
+ gpio_set(GPIOB, GPIO0);
+ }
+
+#if 0
+ byte cycles = 0;
+ for (;;) {
+ gpio_toggle(GPIOC, GPIO13);
+ delay_ms(100);
+ ds_step();
+ if (usart_get_flag(USART1, USART_SR_RXNE)) {
+ uint ch = usart_recv(USART1);
+ if (ch == 'B') {
+ bypass_active = 1;
+ gpio_set(GPIOC, GPIO15); // opto-coupler
+ gpio_clear(GPIOC, GPIO14); // LED
+ } else if (ch == 'b') {
+ bypass_active = 0;
+ gpio_clear(GPIOC, GPIO15); // opto-coupler
+ gpio_set(GPIOC, GPIO14); // LED
+ } else if (ch >= '0' && ch <= '9') {
+ pwm = 3*(ch - '0') + 1;
+ /*
+ * ch pwm %
+ * 0 1 0
+ * 1 4 0
+ * 2 7 18
+ * 3 10 31
+ * 4 13 44
+ * 5 16 57
+ * 6 19 71
+ * 7 22 84
+ * 8 25 97
+ * 9 28 100
+ *
+ * % = pwm*4.389 - 12.723
+ */
+ timer_set_oc_value(TIM4, TIM_OC1, pwm);
+ }
+ }
+ if (cycles++ >= 50) {
+ cycles = 0;
+ show_temperature();
+ }
+ }
+#endif
+
+ return 0;
+}