]> mj.ucw.cz Git - home-hw.git/commitdiff
test-sinclair: Experiments with RC
authorMartin Mares <mj@ucw.cz>
Thu, 13 Jul 2023 14:55:50 +0000 (16:55 +0200)
committerMartin Mares <mj@ucw.cz>
Thu, 13 Jul 2023 14:55:50 +0000 (16:55 +0200)
test-sinclair/README
test-sinclair/main.c

index cdb49b88c433b255843ec3fdced7e635c6be22f6..80095ed933017f7b29babfcc709d75c60b97665b 100644 (file)
@@ -3,6 +3,8 @@ Assignment of peripherals and pins
 
 SPI2   emulated TM1618 LED driver
 USART1 debugging
+TIM3   TM1618 timeout
+TIM4   timing of IR remote control
 
 
                           Blue Pill pinout
@@ -22,7 +24,7 @@ BluePill LED          | PC13           GND |
                        | PB0           PA11 |
                        | PB1           PA10 |  RXD1 - debugging console
                        | PB10           PA9 |  TXD1 - debugging console
-                       | PB11           PA8 |
+                       | PB11           PA8 |  IR remote control
                        | RESET         PB15 |  MOSI2 - LED driver data input
                        | 3.3 V         PB14 |  MISO2 - unused
                        | GND           PB13 |  SCK2 - LED driver clock
index 753bcfd9ffe951f94b4a9b2152792ec6eb69e9dd..29ddb6ad7ef03b12db536444b9233e16ab001302 100644 (file)
@@ -34,6 +34,7 @@ static void clock_init(void)
        rcc_periph_clock_enable(RCC_USART1);
        rcc_periph_clock_enable(RCC_USB);
        rcc_periph_clock_enable(RCC_TIM3);
+       rcc_periph_clock_enable(RCC_TIM4);
 
        rcc_periph_reset_pulse(RST_GPIOA);
        rcc_periph_reset_pulse(RST_GPIOB);
@@ -42,6 +43,7 @@ static void clock_init(void)
        rcc_periph_reset_pulse(RST_USART1);
        rcc_periph_reset_pulse(RST_USB);
        rcc_periph_reset_pulse(RST_TIM3);
+       rcc_periph_reset_pulse(RST_TIM4);
 }
 
 static void gpio_init(void)
@@ -59,6 +61,10 @@ static void gpio_init(void)
        // 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);
+
+       // PA8 = IR remote control
+       gpio_clear(GPIOA, GPIO8);
+       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO8);
 }
 
 static void usart_init(void)
@@ -66,7 +72,7 @@ static void usart_init(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);
+       usart_set_mode(USART1, USART_MODE_TX_RX);
        usart_set_parity(USART1, USART_PARITY_NONE);
        usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
 
@@ -294,6 +300,148 @@ static void tm_show(void)
 #endif
 }
 
+/*** 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
+};
+
+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 char rc_keys[] = "Oohmlsdwc7890123456&*()";
+
+static void rc_init(void)
+{
+       // TIM4 runs at 1 MHz and it is used for timing of RC pulses
+       timer_set_prescaler(TIM4, CPU_CLOCK_MHZ - 1);
+       timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+       timer_update_on_overflow(TIM4);
+       timer_disable_preload(TIM4);
+       timer_one_shot_mode(TIM4);
+       timer_enable_irq(TIM4, TIM_DIER_UIE);
+       nvic_enable_irq(NVIC_TIM4_IRQ);
+}
+
+static volatile const char *rc_pattern_pos;
+static volatile char rc_pending;
+
+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 '#':
+                               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 '$':
+                               val = 0;
+                               duration = 10000;
+                               break;
+                       default:
+                               // End of transmission
+                               rc_pattern_pos = NULL;
+                               rc_pending = 0;
+                               return;
+               }
+
+               if (val)
+                       gpio_set(GPIOA, GPIO8);
+               else
+                       gpio_clear(GPIOA, GPIO8);
+
+               timer_set_period(TIM4, duration - 1);
+               timer_generate_event(TIM4, TIM_EGR_UG);
+               timer_enable_counter(TIM4);
+       }
+}
+
+static bool rc_send(char key)
+{
+       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);
+
+       timer_set_period(TIM4, 1);
+       timer_generate_event(TIM4, TIM_EGR_UG);
+       timer_enable_counter(TIM4);
+       return true;
+}
+
 /*** USB ***/
 
 static usbd_device *usbd_dev;
@@ -496,6 +644,7 @@ int main(void)
        debug_printf("Hello, world!\n");
 
        tm_init();
+       rc_init();
        usb_init();
 
        u32 last_blink = 0;
@@ -507,6 +656,11 @@ int main(void)
                        tm_show();
                }
 
+               if (usart_get_flag(USART1, USART_SR_RXNE)) {
+                       uint ch = usart_recv(USART1);
+                       debug_putc(ch);
+               }
+
                if (usb_event_pending) {
                        usbd_poll(usbd_dev);
                        usb_event_pending = 0;