X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=test-sinclair%2Fmain.c;h=d0194cc28838de19f6eb8b8ef3fcd520fa9a3f2f;hb=ba8282ed5ebe4c43cbf7e78007361e1a3b8cc76a;hp=29ddb6ad7ef03b12db536444b9233e16ab001302;hpb=220044b6da0a65f80bf9734a9c8e8fc577c7aa63;p=home-hw.git diff --git a/test-sinclair/main.c b/test-sinclair/main.c index 29ddb6a..d0194cc 100644 --- a/test-sinclair/main.c +++ b/test-sinclair/main.c @@ -57,14 +57,15 @@ static void gpio_init(void) gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); gpio_clear(GPIOC, GPIO13); - // PB13 = SCK2 - // PB15 = MOSI2 - gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO13); - gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO15); + // PB13 = SCK2 (pulled up) + // PB15 = MOSI2 (pulled up) + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO13); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO15); + gpio_set(GPIOB, GPIO13 | GPIO15); // 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) @@ -302,60 +303,51 @@ static void tm_show(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 +/* + * The AC unit expects demodulated IR signal. The RC sends 52-bit messages + * (plus leader and trailer). The last 4 bits are a complement of checksum + * of 4-bit nibbles. + * + * We represent the messages as two 32-bit words, the upper word containing + */ + +#define RC_POWER_OFF_HI 0b00000000000000000000 +#define RC_POWER_OFF_LO 0b00000000000000010000000010100100 + +#define RC_DEFAULT_HI 0b00000011000000000000 + +// Cooling with different fan settings. Combines with a temperature setting (17-30). +#define RC_COOL_AUTO 0b00000000000000010000000000000000 +#define RC_COOL_HIGH 0b00000000000000010000100000000000 +#define RC_COOL_MED 0b00000000000000010001000100000000 +#define RC_COOL_LOW 0b00000000000000010010001000000000 + +static const u32 rc_cool_fan[4] = { + RC_COOL_AUTO, + RC_COOL_LOW, + RC_COOL_MED, + RC_COOL_HIGH, }; -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*#*$", +// Heating with fixed fan setting. Combines with a temperature setting (15-25). +#define RC_WARM 0b00000000000000010000001100000000 + +// Dehumidifying with fixed fan setting. This is always sent with temperature=17. +#define RC_DEHUMIDIFY 0b00000000000000010010010000000000 + +// This can be added to any command to enable sleep mode, but we do not issue it yet. +#define RC_SLEEP 0b00000000000010000000000000000000 + +enum rc_mode { + MODE_OFF, + MODE_COOL, + MODE_WARM, + MODE_DEHUMIDIFY, }; -static const char rc_keys[] = "Oohmlsdwc7890123456&*()"; +static byte rc_mode = MODE_COOL; // MODE_xxx +static byte rc_fan; // 0-3 +static byte rc_temp = 17; // 15-30 static void rc_init(void) { @@ -369,48 +361,98 @@ 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; + + if (rc_mode == MODE_COOL) { + rc_pattern[1] = rc_cool_fan[rc_fan]; + if (t < 17) + t = 17; + if (t > 30) + t = 30; + } else if (rc_mode == MODE_WARM) { + rc_pattern[1] = RC_WARM; + if (t < 15) + t = 15; + if (t > 25) + t = 25; + } else { + rc_pattern[1] = RC_DEHUMIDIFY; + t = 17; + } + + // Encode temperature + rc_pattern[1] |= (t - 15) << 4; + + // Compute checksum + uint sum = 0; + for (uint i=0; i<2; i++) + for (uint j=0; j<32; j+=4) + sum += (rc_pattern[i] >> j) & 0x0f; + rc_pattern[1] |= (sum & 0x0f) ^ 0x0f; +} 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; + // debug_putc('#'); 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; + // debug_putc('$'); 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; + // debug_putc('*'); + } 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] & (0x80000000 >> (i & 31))) { + duration = 1471; + // debug_putc('B'); + } else { + duration = 480; + // debug_putc('A'); + } + } } + rc_tick++; + if (val) gpio_set(GPIOA, GPIO8); else @@ -422,24 +464,82 @@ void tim4_isr(void) } } -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 (mode=%d, fan=%d, temp=%d)\n", + (uint) rc_pattern[0], (uint) rc_pattern[1], + rc_mode, rc_fan, rc_temp); + 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 ***/ @@ -658,7 +758,17 @@ int main(void) 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) {