#include "util.h"
#include "ds18b20.h"
#include "modbus.h"
+#include "registers.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <string.h>
-static void clock_setup(void)
+static void rc_init(void);
+static bool rc_send(char key);
+
+static void clock_init(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz();
rcc_periph_reset_pulse(RST_TIM4);
}
-static void gpio_setup(void)
+static void gpio_init(void)
{
// Switch JTAG off to free up pins
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
ms_ticks++;
}
-static void tick_setup(void)
+static void tick_init(void)
{
systick_set_frequency(1000, 72000000);
systick_counter_enable();
systick_interrupt_enable();
}
+#if 0
static void delay_ms(uint ms)
{
u32 start_ticks = ms_ticks;
while (ms_ticks - start_ticks < ms)
;
}
+#endif
-static void usart_setup(void)
+static void usart_init(void)
{
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
// 50% PWM for the IR LED
timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_FORCE_HIGH); // will be TIM_OCM_PWM1 when transmitting
- // timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_PWM1); // FIXME
timer_set_oc_value(TIM4, TIM_OC2, T4_CYCLE / 2);
timer_set_oc_polarity_high(TIM4, TIM_OC2);
timer_enable_oc_output(TIM4, TIM_OC2);
int main(void)
{
- clock_setup();
- gpio_setup();
- tick_setup();
- usart_setup();
+ clock_init();
+ gpio_init();
+ tick_init();
+ usart_init();
pwm_init();
+ rc_init();
- debug_puts("Hello, world!\n");
+ debug_puts("Hello, world! This is Aircon Controller speaking.\n");
ds_init();
modbus_init();
- byte cycles = 0;
+ u32 last_show_temp = 0;
+ u32 last_ds_step = 0;
+
for (;;) {
- debug_led_toggle();
- delay_ms(100);
- ds_step();
+ if (ms_ticks - last_ds_step >= 100) {
+ debug_led_toggle();
+ ds_step();
+ last_ds_step = ms_ticks;
+ }
+
modbus_loop();
+
if (usart_get_flag(USART1, USART_SR_RXNE)) {
uint ch = usart_recv(USART1);
if (ch == 'B') {
* % = pwm*4.389 - 12.723
*/
timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
+ } else {
+ rc_send(ch);
}
}
- if (cycles++ >= 50) {
- cycles = 0;
+
+ if (ms_ticks - last_show_temp >= 5000) {
show_temperature();
+ last_show_temp = ms_ticks;
}
+
+ wait_for_interrupt();
}
return 0;
}
-/*** Modbus callbacks ***/
-
-enum aircon_coils {
- AIRCON_COIL_EXCHANGER_BYPASS,
- AIRCON_COIL_MAX,
+/*** Infra-red remote control transmitter ***/
+
+enum rc_keys {
+ RC_AUTO,
+ RC_TEMP_UP,
+ RC_FUNC,
+ RC_HI,
+ RC_TIMER,
+ RC_MID,
+ RC_TEMP_DOWN,
+ RC_SLEEP,
+ RC_LOW,
+ RC_POWER,
+ RC_MAX
};
-enum aircon_input_registers {
- AIRCON_IREG_TEMP_FROM_INSIDE,
- AIRCON_IREG_TEMP_TO_INSIDE,
- AIRCON_IREG_TEMP_FROM_OUTSIDE,
- AIRCON_IREG_TEMP_TO_OUTSIDE,
- AIRCON_IREG_TEMP_MIXED,
- AIRCON_IREG_MAX,
- AIRCON_IREG_DS_ID_BASE = 0x1000,
- AIRCON_IREG_DS_ID_MAX = AIRCON_IREG_DS_ID_BASE + 3*DS_NUM_SENSORS,
+static const char * const rc_patterns[RC_MAX] = {
+ [RC_AUTO] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*B*A*B*A*A*B*B*A*A*B*A*B*B*A*$",
+ [RC_TEMP_UP] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*A*B*A*B*A*A*B*B*B*A*B*A*B*B*A*$",
+ [RC_FUNC] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*B*B*B*A*A*B*A*B*A*A*A*B*B*A*$",
+ [RC_HI] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*A*B*B*A*A*B*A*B*B*A*A*B*B*A*$",
+ [RC_TIMER] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*B*B*B*A*A*B*B*A*A*A*A*B*B*A*$",
+ [RC_MID] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*B*A*B*B*A*A*B*A*A*B*A*A*B*B*A*$",
+ [RC_TEMP_DOWN] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*B*A*B*A*A*B*A*B*A*B*A*B*B*A*$",
+ [RC_SLEEP] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*B*B*B*B*A*A*B*A*A*A*A*A*B*B*A*$",
+ [RC_LOW] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*A*B*B*A*A*B*B*A*B*A*A*B*B*A*$",
+ [RC_POWER] = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*A*B*B*B*A*A*B*B*B*A*A*A*B*B*A*$",
};
-enum aircon_holding_registers {
- AIRCON_HREG_EXCHANGER_FAN,
- AIRCON_HREG_REMOTE_CONTROL,
- AIRCON_HREG_MAX,
-};
+static const char rc_keys[] = "aufhtmdslp";
+
+static void rc_init(void)
+{
+ // TIM1 runs at 1 MHz and it is used for timing of RC pulses
+ timer_set_prescaler(TIM1, CPU_CLOCK_MHZ - 1);
+ timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+ timer_update_on_overflow(TIM1);
+ timer_disable_preload(TIM1);
+ timer_one_shot_mode(TIM1);
+ timer_enable_irq(TIM1, TIM_DIER_UIE);
+ nvic_enable_irq(NVIC_TIM1_UP_IRQ);
+}
+
+static volatile const char *rc_pattern_pos;
+static volatile char rc_pending;
+
+void tim1_up_isr(void)
+{
+ if (TIM_SR(TIM1) & TIM_SR_UIF) {
+ TIM_SR(TIM1) &= ~TIM_SR_UIF;
+
+ if (!rc_pattern_pos) // Just to be sure
+ return;
+
+ bool val; // 1=pulse, 0=break
+ uint duration; // in μs
+
+ switch (*rc_pattern_pos++) {
+ case '^':
+ val = 1;
+ duration = 9032;
+ break;
+ case '#':
+ val = 0;
+ duration = 4457;
+ break;
+ case '*':
+ val = 1;
+ duration = 619;
+ break;
+ case 'A':
+ val = 0;
+ duration = 514;
+ break;
+ case 'B':
+ val = 0;
+ duration = 1617;
+ break;
+ case '$':
+ val = 0;
+ duration = 10000;
+ break;
+ default:
+ // End of transmission
+ gpio_set(GPIOC, GPIO13);
+ rc_pattern_pos = NULL;
+ rc_pending = 0;
+ return;
+ }
+
+ if (val)
+ timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_PWM1);
+ else
+ timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_FORCE_HIGH);
+
+ timer_set_period(TIM1, duration - 1);
+ timer_generate_event(TIM1, TIM_EGR_UG);
+ timer_enable_counter(TIM1);
+ }
+}
+
+static bool rc_send(char key)
+{
+ if (rc_pending)
+ return false;
+
+ const char *s = strchr(rc_keys, key);
+ if (!s)
+ return false;
+ rc_pending = key;
+ rc_pattern_pos = rc_patterns[s - rc_keys];
+ debug_printf("RC sending: %c", key);
+
+ gpio_clear(GPIOC, GPIO13);
+
+ timer_set_period(TIM1, 1);
+ timer_generate_event(TIM1, TIM_EGR_UG);
+ timer_enable_counter(TIM1);
+ return true;
+}
+
+/*** Modbus callbacks ***/
bool modbus_check_discrete_input(u16 addr UNUSED)
{
}
}
+#define AIRCON_IREG_DS_ID_MAX (AIRCON_IREG_DS_ID_BASE + 3*DS_NUM_SENSORS)
+
bool modbus_check_input_register(u16 addr)
{
- return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX);
+ return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_BASE);
}
static const byte temp_sensor_addrs[][8] = {
return 0x8000;
if (ds_sensors[i].current_temp == DS_TEMP_UNKNOWN)
return 0x8000;
- return ds_sensors[i].current_temp & 0xffff;
+ return ((ds_sensors[i].current_temp + 5) / 10) & 0xffff;
} else if (addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX) {
byte i = (addr - AIRCON_IREG_DS_ID_BASE) / 3;
byte j = (addr - AIRCON_IREG_DS_ID_BASE) % 3;
case AIRCON_HREG_EXCHANGER_FAN:
return fan_pwm;
case AIRCON_HREG_REMOTE_CONTROL:
+ return rc_pending;
default:
return 0;
}
timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
break;
case AIRCON_HREG_REMOTE_CONTROL:
+ if (!rc_send(value))
+ modbus_slave_error();
break;
default:
;