From: Martin Mares Date: Sat, 13 May 2023 21:17:32 +0000 (+0200) Subject: test-display2: Will play with IR receiver X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=6e7c67fbb1ac2c1b1e51c997046acbc0fb447cdc;p=home-hw.git test-display2: Will play with IR receiver --- diff --git a/test-display2/Makefile b/test-display2/Makefile new file mode 100644 index 0000000..8063dfb --- /dev/null +++ b/test-display2/Makefile @@ -0,0 +1,11 @@ +ROOT=.. +BINARY=test +OBJS=main.o +LIB_OBJS=util-debug.o + +WITH_BOOT_LOADER=1 +WITH_DFU_FLASH=1 +DFU_ARGS=-d 4242:0007 +# DFU_ARGS=-d 4242:0008 + +include $(ROOT)/mk/bluepill.mk diff --git a/test-display2/README b/test-display2/README new file mode 100644 index 0000000..19ac2c9 --- /dev/null +++ b/test-display2/README @@ -0,0 +1,30 @@ +Assignment of peripherals and pins +================================== + +I2C1 display +USART1 debugging + + + Blue Pill pinout + +--------------------+ + | VBATT 3.3V | +BluePill LED | PC13 GND | display power + | PC14 5V | display power (white side of connector) + | PC15 PB9 | + | PA0 PB8 | + | PA1 PB7 | SDA1 display (white side of connector) + | PA2 PB6 | SCL1 display + | PA3 PB5 | + | PA4 PB4 | + | PA5 PB3 | + | PA6 PA15 | + | PA7 PA12 | + | PB0 PA11 | + | PB1 PA10 | RXD1 - debugging console + | PB10 PA9 | TXD1 - debugging console + | PB11 PA8 | SFH5110 output (white side of connector) + | RESET PB15 | + | 3.3 V PB14 | + | GND PB13 | + | GND PB12 | + +--------------------+ diff --git a/test-display2/config.h b/test-display2/config.h new file mode 100644 index 0000000..fe39e28 --- /dev/null +++ b/test-display2/config.h @@ -0,0 +1,18 @@ +/* + * Test Gadget -- Configuration + * + * (c) 2020 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 72 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL + +// Testing of IR receiver + +#undef IR_TEST diff --git a/test-display2/host/Makefile b/test-display2/host/Makefile new file mode 100644 index 0000000..5e6e00c --- /dev/null +++ b/test-display2/host/Makefile @@ -0,0 +1,12 @@ +UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw) +UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw) + +CFLAGS=-std=gnu99 -O2 -Wall -Wextra -Wno-parentheses $(UCWCF) +LDLIBS=-lusb-1.0 $(UCWLF) + +all: test + +test: test.c + +clean: + rm -f test diff --git a/test-display2/host/test.c b/test-display2/host/test.c new file mode 100644 index 0000000..cad8474 --- /dev/null +++ b/test-display2/host/test.c @@ -0,0 +1,104 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +struct libusb_context *usb_ctxt; +struct libusb_device_handle *devh; + +static libusb_device *find_device(void) +{ + libusb_device **devlist; + ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist); + if (devn < 0) + { + fprintf(stderr, "Cannot enumerate USB devices: error %d\n", (int) devn); + exit(1); + } + + for (ssize_t i=0; itm_hour / 10, + tm->tm_hour % 10, + tm->tm_min / 10, + tm->tm_min % 10, + (tm->tm_sec % 2 ? 0xff : 0), + }; + int transferred; + if (err = libusb_bulk_transfer(devh, 0x01, req, 5, &transferred, 1000)) + die("Transfer failed: error %d", err); + // printf("Transferred %d bytes\n", transferred); + +#if 0 + unsigned char resp[64]; + int received; + if (err = libusb_bulk_transfer(devh, 0x82, resp, 64, &received, 2000)) + die("Receive failed: error %d", err); + // printf("Received %d bytes\n", received); + if (received >= 12) + { + int t = get_u32_be(resp); + int p = get_u32_be(resp + 4); + uint cnt = get_u32_be(resp + 8); + msg(L_INFO, "Temperature %d ddegC, pressure %d Pa, cnt %u", t, p, cnt); + } +#endif + + sleep(1); + } + + return 0; +} diff --git a/test-display2/main.c b/test-display2/main.c new file mode 100644 index 0000000..4fb1590 --- /dev/null +++ b/test-display2/main.c @@ -0,0 +1,518 @@ +/* + * Workshop Clock + * + * (c) 2020 Martin Mareš + */ + +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/*** Hardware init ***/ + +static void clock_init(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_I2C1); + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USB); + + rcc_periph_reset_pulse(RST_GPIOA); + rcc_periph_reset_pulse(RST_GPIOB); + rcc_periph_reset_pulse(RST_GPIOC); + rcc_periph_reset_pulse(RST_I2C1); + rcc_periph_reset_pulse(RST_USART1); + rcc_periph_reset_pulse(RST_USB); +} + +static void gpio_init(void) +{ + // PA9 = TXD1 for debugging console + // PA10 = RXD1 for debugging console + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10); + + // PC13 = BluePill LED + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); + gpio_clear(GPIOC, GPIO13); + + // PB7 = SDA for display controller + // PB6 = SCL for display controller + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO6 | GPIO7); + + // PB8 = SFH5110 output (5V tolerant) + gpio_set_mode(GPIOC, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, 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_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + + usart_enable(USART1); +} + +/*** System ticks ***/ + +static volatile u32 ms_ticks; + +void sys_tick_handler(void) +{ + ms_ticks++; +} + +static void tick_init(void) +{ + systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000); + systick_counter_enable(); + systick_interrupt_enable(); +} + +static void delay_ms(uint ms) +{ + u32 start_ticks = ms_ticks; + while (ms_ticks - start_ticks < ms) + ; +} + +/*** Display ***/ + +/* + * Display digits: + * + * ---- 40 ---- + * | | + * | | + * 20 80 + * | | + * | | + * ---- 10 ---- + * | | + * | | + * 08 02 + * | | + * | | + * ---- 04 ---- + * (01) + */ + +static byte disp[4]; +static byte ctrl = 0x56; + +static void display_update(void) +{ + debug_puts("Display update\n"); + byte cmds[4]; + cmds[0] = 0; + cmds[1] = ctrl; + cmds[2] = ((disp[1] & 0x88) >> 3) | ((disp[1] & 0x44) >> 1) | ((disp[1] & 0x22) << 1) | ((disp[1] & 0x11) << 3); + cmds[3] = disp[0]; + i2c_transfer7(I2C1, 0x76/2, (byte *) cmds, sizeof(cmds), NULL, 0); + + cmds[2] = ((disp[3] & 0x88) >> 3) | ((disp[3] & 0x44) >> 1) | ((disp[3] & 0x22) << 1) | ((disp[3] & 0x11) << 3); + cmds[3] = disp[2]; + i2c_transfer7(I2C1, 0x70/2, (byte *) cmds, sizeof(cmds), NULL, 0); + + debug_puts("Update done\n"); +} + +static void display_init(void) +{ + debug_puts("I2C init\n"); + i2c_peripheral_disable(I2C1); + i2c_set_speed(I2C1, i2c_speed_sm_100k, rcc_apb1_frequency / 1000000); + i2c_peripheral_enable(I2C1); + + disp[0] = 0x82; + disp[1] = 0xdc; + disp[2] = 0xd6; + disp[3] = 0xb2; + display_update(); +} + +static void display_test(void) +{ + static byte mode; + + disp[0] ^= 0x01; + display_update(); + +#if 0 + byte cmds[] = { 0x00, mode ? 0x77 : 0x77 }; + i2c_transfer7(I2C1, 0x70/2, (byte *) cmds, sizeof(cmds), NULL, 0); +#endif + +#if 0 + byte disp[] = { 0xff, 0xff, mode ? 0xff : 0x00, mode ? 0xff : 0xff }; + byte cmds[] = { 0x00, 0x77, 0, 0, 0, 0 }; + cmds[2] = (disp[0] & 0xf0) | (disp[2] >> 4); + cmds[3] = (disp[1] & 0xf0) | (disp[3] >> 4); + cmds[4] = (disp[2] & 0x0f) | (disp[0] << 4); + cmds[5] = (disp[3] & 0x0f) | (disp[1] << 4); + i2c_transfer7(I2C1, 0x70/2, (byte *) cmds, sizeof(cmds), NULL, 0); +#endif + + mode = !mode; +} + +static const byte lcd_font[] = { + [0] = 0xee, + [1] = 0x82, + [2] = 0xdc, + [3] = 0xd6, + [4] = 0xb2, + [5] = 0x76, + [6] = 0x7e, + [7] = 0xc2, + [8] = 0xfe, + [9] = 0xf6, + [10] = 0xea, + [11] = 0x3e, + [12] = 0x6c, + [13] = 0x9e, + [14] = 0x7c, + [15] = 0x78, +}; + +/*** USB ***/ + +static usbd_device *usbd_dev; + +enum usb_string { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIAL, +}; + +static char usb_serial_number[13]; + +static const char *usb_strings[] = { + "United Computer Wizards", + "Workshop Clock", + usb_serial_number, +}; + +static const struct usb_device_descriptor device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x4242, + .idProduct = 0x0007, + .bcdDevice = 0x0000, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = 1, +}; + +static const struct usb_endpoint_descriptor endpoints[] = {{ + // Bulk end-point for sending values to the display + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, +}}; + +static const struct usb_interface_descriptor iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = endpoints, +}; + +static const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof(struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x0100, +}; + +static const struct usb_interface_descriptor dfu_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + .iInterface = 0, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), +}; + +static const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .altsetting = &iface, +}, { + .num_altsetting = 1, + .altsetting = &dfu_iface, +}}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 50, // multiplied by 2 mA + .interface = ifaces, +}; + +static byte usb_configured; +static uint8_t usbd_control_buffer[64]; + +static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED) +{ + // Reset to bootloader, which implements the rest of DFU + debug_printf("Switching to DFU\n"); + debug_flush(); + scb_reset_core(); +} + +static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED, + struct usb_setup_data *req, + uint8_t **buf UNUSED, + uint16_t *len UNUSED, + void (**complete)(usbd_device *dev, struct usb_setup_data *req)) +{ + if (req->bmRequestType != 0x21 || req->bRequest != DFU_DETACH) + return USBD_REQ_NOTSUPP; + + *complete = dfu_detach_complete; + return USBD_REQ_HANDLED; +} + +static void ep01_cb(usbd_device *dev, uint8_t ep UNUSED) +{ + // We received a frame from the USB host + byte buf[8]; + uint len = usbd_ep_read_packet(dev, 0x01, buf, 8); + debug_printf("USB: Host sent %u bytes\n", len); + if (len >= 5) { + for (uint i=0; i<4; i++) { + if (buf[i] < 16) + disp[i] = lcd_font[buf[i]]; + else + disp[i] = 0; + } + if (buf[4]) + disp[1] |= 1; + display_update(); + } +} + +static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED) +{ + usbd_register_control_callback( + dev, + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + dfu_control_cb); + usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, ep01_cb); + usb_configured = 1; +} + +static void reset_cb(void) +{ + debug_printf("USB: Reset\n"); + usb_configured = 0; +} + +static volatile bool usb_event_pending; + +void usb_lp_can_rx0_isr(void) +{ + /* + * We handle USB in the main loop to avoid race conditions between + * USB interrupts and other code. However, we need an interrupt to + * up the main loop from sleep. + * + * We set up only the low-priority ISR, because high-priority ISR handles + * only double-buffered bulk transfers and isochronous transfers. + */ + nvic_disable_irq(NVIC_USB_LP_CAN_RX0_IRQ); + usb_event_pending = 1; +} + +static void usb_init(void) +{ + // Simulate USB disconnect + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12); + gpio_clear(GPIOA, GPIO11 | GPIO12); + delay_ms(100); + + usbd_dev = usbd_init( + &st_usbfs_v1_usb_driver, + &device, + &config, + usb_strings, + ARRAY_SIZE(usb_strings), + usbd_control_buffer, + sizeof(usbd_control_buffer) + ); + usbd_register_reset_callback(usbd_dev, reset_cb); + usbd_register_set_config_callback(usbd_dev, set_config_cb); + usb_event_pending = 1; +} + +/*** Testing of IR receiver ***/ + +#ifdef IR_TEST + +static u16 get_bit(void) +{ +#if 1 + return !gpio_get(GPIOB, GPIO8); +#else + int x = 0; + x += !gpio_get(GPIOB, GPIO8); + x += !gpio_get(GPIOB, GPIO8); + x += !gpio_get(GPIOB, GPIO8); + x += !gpio_get(GPIOB, GPIO8); + return x >= 2; +#endif +} + +#define MAX_SAMPLES 1024 +u32 samples[MAX_SAMPLES]; + +static void ir_test_loop(void) +{ + debug_puts("\n\n### Infrared Remote Control receiver ###\n\n"); + + systick_set_reload(0xffffff); + systick_counter_enable(); + systick_interrupt_disable(); + systick_clear(); + + for (;;) { + gpio_set(GPIOC, GPIO13); + + if (get_bit()) { + debug_puts("Waiting for silence\n"); + while (get_bit()) + ; + } + debug_puts("Ready..."); + + u16 last = 0; + uint nsamp = 0; + u32 start; + + do { + start = systick_get_value(); + last = get_bit(); + } while (!last); + + gpio_clear(GPIOC, GPIO13); + + for (;;) { + u32 now; + u16 curr; + uint len; + do { + now = systick_get_value(); + len = (start - now) & 0xffffff; + if (len > 5000000) { + samples[nsamp++] = len; + goto timeout; + } + curr = get_bit(); + } while (curr == last); + samples[nsamp++] = len; + if (nsamp >= MAX_SAMPLES) + break; + start = now; + last = curr; + } + + timeout: + for (uint i=0; i= 500) { + debug_led_toggle(); + last_blink = ms_ticks; + display_test(); + } + + if (usb_event_pending) { + usbd_poll(usbd_dev); + usb_event_pending = 0; + nvic_clear_pending_irq(NVIC_USB_LP_CAN_RX0_IRQ); + nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); + } + + wait_for_interrupt(); + } + + return 0; +}