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);
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)
// 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)
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);
#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;
debug_printf("Hello, world!\n");
tm_init();
+ rc_init();
usb_init();
u32 last_blink = 0;
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;