// PA8 = IR remote control
gpio_clear(GPIOA, GPIO8);
- gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO8);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
}
static void usart_init(void)
/*** Infra-red remote control simulator ***/
-enum rc_keys {
- RC_POWER_OFF,
- RC_POWER_ON,
- RC_HIGH,
- RC_MED,
- RC_LO,
- RC_SLEEP,
- RC_DRY,
- RC_WARM,
- RC_COLD,
- RC_T17,
- RC_T18,
- RC_T19,
- RC_T20,
- RC_T21,
- RC_T22,
- RC_T23,
- RC_T24,
- RC_T25,
- RC_T26,
- RC_T27,
- RC_T28,
- RC_T29,
- RC_T30,
- RC_MAX
+#define RC_POWER_OFF_HI 0b00000000000000000000
+#define RC_POWER_OFF_LO 0b00000000000000010000000010100100
+
+#define RC_DEFAULT_HI 0b00000011000000000000
+
+// Various temperatures
+#define RC_17_COOL_AUTO 0b00000000000000010000000000101001
+#define RC_18_COOL_AUTO 0b00000000000000010000000000111000
+#define RC_19_COOL_AUTO 0b00000000000000010000000001000111
+#define RC_20_COOL_AUTO 0b00000000000000010000000001010110
+#define RC_21_COOL_AUTO 0b00000000000000010000000001100101
+#define RC_22_COOL_AUTO 0b00000000000000010000000001110100
+#define RC_23_COOL_AUTO 0b00000000000000010000000010000011
+#define RC_24_COOL_AUTO 0b00000000000000010000000010010010
+#define RC_25_COOL_AUTO 0b00000000000000010000000010100001
+#define RC_26_COOL_AUTO 0b00000000000000010000000010110000
+#define RC_27_COOL_AUTO 0b00000000000000010000000011001111
+#define RC_28_COOL_AUTO 0b00000000000000010000000011011110
+#define RC_29_COOL_AUTO 0b00000000000000010000000011101101
+#define RC_30_COOL_AUTO 0b00000000000000010000000011111100
+
+// Various fan settings
+#define RC_17_COOL_HI 0b00000000000000010000100000100001
+#define RC_17_COOL_MED 0b00000000000000010001000100100111
+#define RC_17_COOL_LO 0b00000000000000010010001000100101
+
+// Stand-alone, no temperature nor fan setting
+#define RC_DEHUMIDIFY 0b00000000000000010010010000100011
+
+// Combines with a temperature setting, but no fan setting
+#define RC_17_WARM 0b00000000000000010000001100100110
+
+// All modes have a sleep variant, which we do not use yet
+#define RC_17_COOL_AUTO_SLEEP 0b00000000000010010000000000100001
+
+static const u32 rc_fan_settings[4] = {
+ 0,
+ RC_17_COOL_LO ^ RC_17_COOL_AUTO,
+ RC_17_COOL_MED ^ RC_17_COOL_AUTO,
+ RC_17_COOL_HI ^ RC_17_COOL_AUTO,
};
-static const char * const rc_patterns[RC_MAX] = {
- [RC_POWER_OFF] = "*#*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*B*A*A*B*A*A*#*$",
- [RC_POWER_ON] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*B*A*A*A*A*B*#*$",
- [RC_HIGH] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*B*A*A*A*B*A*B*A*B*A*A*B*#*$",
- [RC_MED] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*B*A*A*A*B*B*A*B*A*B*B*B*B*#*$",
- [RC_LO] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*B*A*A*A*B*A*B*A*B*A*B*B*A*B*#*$",
- [RC_SLEEP] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*B*A*A*B*A*A*A*B*A*B*A*B*A*A*B*A*B*#*$",
- [RC_DRY] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*B*A*A*B*A*A*B*A*B*A*B*A*B*B*#*$",
- [RC_WARM] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*B*B*B*A*B*A*B*B*B*A*#*$",
- [RC_COLD] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*B*A*A*A*A*B*#*$",
- [RC_T17] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*A*B*A*B*A*A*B*#*$",
- [RC_T18] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*A*B*B*B*A*A*A*#*$",
- [RC_T19] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*B*A*A*A*B*B*B*#*$",
- [RC_T20] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*B*A*B*A*B*B*A*#*$",
- [RC_T21] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*B*B*A*A*B*A*B*#*$",
- [RC_T22] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*A*B*B*B*A*B*A*A*#*$",
- [RC_T23] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*A*A*A*A*B*B*#*$",
- [RC_T24] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*A*B*A*A*B*A*#*$",
- [RC_T25] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*B*A*A*A*A*B*#*$",
- [RC_T26] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*A*B*B*A*A*A*A*#*$",
- [RC_T27] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*B*A*A*B*B*B*B*#*$",
- [RC_T28] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*B*A*B*B*B*B*A*#*$",
- [RC_T29] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*B*B*A*B*B*A*B*#*$",
- [RC_T30] = "*#*A*A*A*A*A*A*B*B*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*A*B*A*A*A*A*A*A*A*A*B*B*B*B*B*B*A*A*#*$",
+static const u32 rc_temp_settings[14] = {
+#define X(t) RC_##t##_COOL_AUTO ^ RC_17_COOL_AUTO
+ X(17),
+ X(18),
+ X(19),
+ X(20),
+ X(21),
+ X(22),
+ X(23),
+ X(24),
+ X(25),
+ X(26),
+ X(27),
+ X(28),
+ X(29),
+ X(30),
+#undef X
};
-static const char rc_keys[] = "Oohmlsdwc7890123456&*()";
+enum rc_mode {
+ MODE_OFF,
+ MODE_COOL,
+ MODE_WARM,
+ MODE_DEHUMIDIFY,
+};
+
+static byte rc_mode; // MODE_xxx
+static byte rc_fan; // 0-3
+static byte rc_temp; // 17-30
static void rc_init(void)
{
nvic_enable_irq(NVIC_TIM4_IRQ);
}
-static volatile const char *rc_pattern_pos;
-static volatile char rc_pending;
+static u32 rc_pattern[2];
+static uint rc_tick;
+
+static void rc_encode(void)
+{
+ if (rc_mode == MODE_OFF) {
+ rc_pattern[0] = RC_POWER_OFF_HI;
+ rc_pattern[1] = RC_POWER_OFF_LO;
+ return;
+ }
+
+ rc_pattern[0] = RC_DEFAULT_HI;
+ uint t = rc_temp, f = rc_fan;
+
+ if (rc_mode == MODE_COOL) {
+ rc_pattern[1] = RC_17_COOL_AUTO;
+ } else if (rc_mode == MODE_WARM) {
+ rc_pattern[1] = RC_17_WARM;
+ f = 0;
+ } else {
+ rc_pattern[1] = RC_DEHUMIDIFY;
+ f = t = 0;
+ }
+
+ rc_pattern[1] ^= rc_fan_settings[f];
+
+ if (t >= 17 && t < 17 + ARRAY_SIZE(rc_temp_settings))
+ rc_pattern[1] ^= rc_temp_settings[t - 17];
+}
void tim4_isr(void)
{
if (TIM_SR(TIM4) & TIM_SR_UIF) {
TIM_SR(TIM4) &= ~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 '#':
+ switch (rc_tick++) {
+ case 0:
+ // Better be safe
+ return;
+ case 2:
+ case 108:
+ // Initial / final marker
val = 0;
duration = 3600;
break;
- case '*':
- val = 1;
- duration = 565;
- break;
- case 'A':
- val = 0;
- duration = 480;
- break;
- case 'B':
- val = 0;
- duration = 1471;
- break;
- case '$':
+ case 110:
+ // Inter-packet gap
val = 0;
duration = 10000;
break;
- default:
- // End of transmission
- rc_pattern_pos = NULL;
- rc_pending = 0;
+ case 111:
+ // End of message
+ rc_tick = 0;
return;
+ default:
+ if (rc_tick % 2) {
+ val = 1;
+ duration = 565;
+ } else {
+ // Even ticks 4 to 106 transmit 52 bits of data
+ uint i = 12 + (rc_tick - 4) / 2;
+ val = 0;
+ if (rc_pattern[i>>5] & (1 << (i & 31)))
+ duration = 1471;
+ else
+ duration = 480;
+ }
}
if (val)
}
}
-static bool rc_send(char key)
+static void rc_send(void)
{
- if (rc_pending)
- return false;
- if (!key)
- 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\n", key);
+ if (rc_tick)
+ return;
+
+ rc_encode();
+ debug_printf("RC sending: %05x %08x\n", (uint) rc_pattern[0], (uint) rc_pattern[1]);
+ rc_tick = 1;
timer_set_period(TIM4, 1);
timer_generate_event(TIM4, TIM_EGR_UG);
timer_enable_counter(TIM4);
- return true;
+}
+
+static bool rc_key(char key)
+{
+ if (key == 'o') {
+ rc_mode = MODE_OFF;
+ rc_send();
+ return true;
+ } else if (key == 'c') {
+ rc_mode = MODE_COOL;
+ rc_send();
+ return true;
+ } else if (key == 'w') {
+ rc_mode = MODE_WARM;
+ rc_send();
+ return true;
+ } else if (key == 'd') {
+ rc_mode = MODE_DEHUMIDIFY;
+ rc_send();
+ return true;
+ } else if (key == 'a') {
+ rc_fan = 0;
+ rc_send();
+ return true;
+ } else if (key == 'l') {
+ rc_fan = 1;
+ rc_send();
+ return true;
+ } else if (key == 'm') {
+ rc_fan = 2;
+ rc_send();
+ return true;
+ } else if (key == 'h') {
+ rc_fan = 3;
+ rc_send();
+ return true;
+ } else if (key >= '7' && key <= '9') {
+ rc_temp = key - '0' + 10;
+ rc_send();
+ return true;
+ } else if (key >= '0' && key <= '6') {
+ rc_temp = key - '0' + 20;
+ rc_send();
+ return true;
+ } else if (key == '&') {
+ rc_temp = 27;
+ rc_send();
+ return true;
+ } else if (key == '*') {
+ rc_temp = 28;
+ rc_send();
+ return true;
+ } else if (key == '(') {
+ rc_temp = 29;
+ rc_send();
+ return true;
+ } else if (key == ')') {
+ rc_temp = 30;
+ rc_send();
+ return true;
+ }
+ return false;
}
/*** USB ***/
if (usart_get_flag(USART1, USART_SR_RXNE)) {
uint ch = usart_recv(USART1);
- debug_putc(ch);
+#if 0
+ if (ch == '1')
+ gpio_set(GPIOA, GPIO8);
+ else if (ch == '0')
+ gpio_clear(GPIOA, GPIO8);
+#else
+ if (rc_key(ch))
+ ;
+#endif
+ else
+ debug_putc(ch);
}
if (usb_event_pending) {