Zephyr hci_uart on nRF52840 with FT232R

Hello,

I've been testing HCI_UART from Zephyr RTOS on nRF52840DK and own device which has nRF52840 + FT232R as USB-UART converter. Regarding the own device it's a circuit long tested working with other firmware and never had issues with how UART bahaves. Flow control is connected, normally everything is golden, even Nordic DFU works and flow control is a necessity for it.

My setup is the following:

  • Ubuntu 18.04, kernel 5.3.0-42
  • Bluez tested both 5.48 and 5.45
  • My default laptop Bluetooth device turned off by sudo hciconfig hci0 down just in case, to not interfere

When flashing Zephyr HCI_UART onto the nRF52840DK im attaching it by btattach -B /dev/ttyUSB1 -S 1000000 -P h4 later use btmon and I'm able to scan for BLE devices which is my first test if a HCI device is operating properly. On the other hand when flashing the same FW (after remapping the pins to my board, that is: tx-pin = <6>; rx-pin = <8>; rts-pin = <26>; cts-pin = <4>;) I start to see a strange error which I haven't been able to debug yet. After attaching my device my dmesg output is flooded by hci1: Frame reassembly failed (-84) and btmon output shows:

= New Index: 00:00:00:00:00:00 (Primary,UART,hci1) [hci1] 171.230779
= Open Index: 00:00:00:00:00:00 [hci1] 171.230791
< HCI Command: Reset (0x03|0x0003) plen 0 #1 [hci1] 171.232583
= Close Index: 00:00:00:00:00:00 [hci1] 181.394212

So it doesn't even reset.

Same happens when I don't set -P h4 and just use -R flag. All the resources I've found so far were related to rPi where people have encountered this issue but didn't find anything particularly useful. On devzone I've found https://devzone.nordicsemi.com/f/nordic-q-a/19295/zephyr-hci-uart-on-nrf52dk---hci-interface-bring-up-fails and https://devzone.nordicsemi.com/f/nordic-q-a/25936/zephyr-hci-uart-on-pca10040-with-linux-controller which being bit different show some resemblance to my experiences. Unfortunately I've haven't found my answer in these threads as well.

I've tried lowering the baud rates suspecting that FT232R has issues or even UART lines start doing funny stuff but with exactly the same results. Debugging with usbmon + wireshark showed me that there was some exchanges going back and forth but given that HCI protocol is quite new to me I didn't yet debug it's contents, just wanted to see if both sides exchange some data - which they are. Also occasionally btmon has thrown some errors about parsing incoming events which would indicate that there is traffic but it's wrong.

Afterwards my plan was to try to replicate similar behavior on the nRF52840 DK but this also failed. I could get different problems but not this one.

So after few hours of back and forth with trying everything I started suspecting that FTDI or it's driver might be doing some shenanigans. For now I haven't found any indication that Zephyr hci_uart could work while passing through an FTDI chip. Also https://www.ftdichip.com/Support/FAQs.htm#HwGen3 states that "Customers shoud be made aware the FTxxx is a USB device and not a "normal" RS232 device as seen on a PC. As such the device operates on a packet basis as opposed to a byte basis." which doesn't say much to me at the moment but sound suspicious.

This is the last difference I can see which could result in problems with parsing on Linux side, namely that nRF DK mounts as /dev/ttyACMx and my device as /dev/ttyUSBx. This might or might not be the cause which is hard to say at this moment, especially given that Larid BT860 module documents HCI working behind a FTDI chip but probably the implementation of the HCI stack is different in some way which takes FTDI on the way into consideration.

Did anyone here experience such issue or has any hints on what I could check?

  • Hi,

    First of all, I wonder why you use the FTDI instead of the USB on the nRF52840 and the HCI over USB sample?

    Regarding the issue, It should just work out of the box I believe. The first thing that comes to mind is that perhaps you are configuring the 32 kHz crystal (which is present on the DK), but don't have that on your custom board? If so, you must use the LFRC instead.

  • Hello Einar,

    The project in terms of hardware was a evolution from nRF52832 based project so when nRF52840 came out we did not want to implement full USB stack and port currently existing firmware to support nRF52840 focusing on it's new BLE features. So while getting rid of FTDI is on the roadmap it wasn't really a priority.

    Crystal wise both are mounted in the design. Today I've tested on a different design which instead of FTDI has an STM controller which acts as intermediary for UART along other things and enumerates as CDC /dev/ttyACMx device using ST USB stack. It wasn't possible to replicate the FDTI problems but this also doesn't work though this time the frame was too short according to btmon.

    So maybe it's not related to the USB conversion after all but HCI_UART relying on one of GPIOs on DK for some reason? There was nothing in the overlay file which I've just added my UART pin definitions but the rest of DTS is quite extensive and a bit intimidating get into when starting with Zephyr

  • Hi,

    Have you made any progress? The HCI_UART sample does not rely on other GPIO pins. It is interesting that you see different behavior using a different USB-UART bridge, but I do not understand what is going on.

  • 0
    21 pts.
    in reply to PB

    Hi,

    I've tried building own device tree based on nRF52840 after noticing that arduino_header uses some of the same GPIOs i'm trying to use for UART. Probably it's not relevant until the actual firmware uses the GPIO - which in case of HCI it doesn't - but still considered that worth trying. Also turned on RTT debugging in the project but so far nothing seems to make it work. Just like UART would not receive anything on the device:

    *** Booting Zephyr OS build zephyr-v2.3.0-359-g8e4faab30a50  ***
    
    [00:00:00.000,000] <dbg> hci_uart.hci_uart_init: 
    [00:00:00.000,000] <dbg> hci_uart.main: Start
    [00:00:00.000,000] <inf> bt_hci_raw: Bluetooth enabled in RAW mode

    Nothing else happens afterwards unless I glitch the UART pins when in RTT the firmware throws:

    [00:02:06.777,465] <err> bt_hci_raw: Unknown H4 type 4
    [00:02:06.864,013] <dbg> hci_uart.h4_read: read 1 remaining 0
    [00:02:06.864,044] <err> bt_hci_raw: Unknown H4 type 4
    [00:02:06.870,819] <dbg> hci_uart.h4_read: read 1 remaining

    The same thing happens when I connect to the USB port via screen and type in some random characters - this would confirm that the nRF chip actually receives the data over FTDI chip.

    The DTM is just a stripped down version of zephyr/boards/arm/nrf52840dk_nrf52840.dtm one:

    /dts-v1/;
    
    / {
    	#address-cells = < 0x1 >;
    	#size-cells = < 0x1 >;
    	model = "Custom board";
    	compatible = "nordic,my_board";
    	chosen {
    		zephyr,entropy = &rng;
    		zephyr,flash-controller = &flash_controller;
    		zephyr,console = &uart0;
    		zephyr,shell-uart = &uart0;
    		zephyr,uart-mcumgr = &uart0;
    		zephyr,bt-mon-uart = &uart0;
    		zephyr,bt-c2h-uart = &uart0;
    		zephyr,sram = &sram0;
    		zephyr,flash = &flash0;
    		zephyr,code-partition = &slot0_partition;
    	};
    	aliases {
    		i2c-0 = &i2c0;
    		i2c-1 = &i2c1;
    		spi-0 = &spi0;
    		spi-1 = &spi1;
    		spi-2 = &spi2;
    		spi-3 = &spi3;
    		qspi-0 = &qspi;
    		uart-0 = &uart0;
    		uart-1 = &uart1;
    		adc-0 = &adc;
    		gpio-0 = &gpio0;
    		gpio-1 = &gpio1;
    		gpiote-0 = &gpiote;
    		wdt-0 = &wdt;
    		usbd-0 = &usbd;
    		cc310 = &cryptocell;
    		arm-cryptocell-310 = &cryptocell310;
    		pwm-0 = &pwm0;
    		pwm-1 = &pwm1;
    		pwm-2 = &pwm2;
    		pwm-3 = &pwm3;
    		qdec-0 = &qdec;
    		rtc-0 = &rtc0;
    		rtc-1 = &rtc1;
    		rtc-2 = &rtc2;
    		timer-0 = &timer0;
    		timer-1 = &timer1;
    		timer-2 = &timer2;
    		timer-3 = &timer3;
    		timer-4 = &timer4;
    		led0 = &led0;
    		led1 = &led1;
    		led2 = &led2;
    		led3 = &led3;
    		pwm-led0 = &pwm_led0;
    		sw0 = &button0;
    		sw1 = &button1;
    		sw2 = &button2;
    		sw3 = &button3;
    	};
    	soc {
    		#address-cells = < 0x1 >;
    		#size-cells = < 0x1 >;
    		compatible = "nordic,nRF52840-QIAA", "nordic,nRF52840", "nordic,nRF52", "simple-bus";
    		interrupt-parent = < &nvic >;
    		ranges;
    		nvic: interrupt-controller@e000e100 {
    			compatible = "arm,v7m-nvic";
    			reg = < 0xe000e100 0xc00 >;
    			interrupt-controller;
    			#interrupt-cells = < 0x2 >;
    			arm,num-irq-priority-bits = < 0x3 >;
    			phandle = < 0x1 >;
    		};
    		systick: timer@e000e010 {
    			compatible = "arm,armv7m-systick";
    			reg = < 0xe000e010 0x10 >;
    			status = "disabled";
    		};
    		flash_controller: flash-controller@4001e000 {
    			compatible = "nordic,nrf52-flash-controller";
    			reg = < 0x4001e000 0x1000 >;
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x1 >;
    			label = "NRF_FLASH_DRV_NAME";
    			flash0: flash@0 {
    				compatible = "soc-nv-flash";
    				label = "NRF_FLASH";
    				erase-block-size = < 0x1000 >;
    				write-block-size = < 0x4 >;
    				reg = < 0x0 0x100000 >;
    				partitions {
    					compatible = "fixed-partitions";
    					#address-cells = < 0x1 >;
    					#size-cells = < 0x1 >;
    					boot_partition: partition@0 {
    						label = "mcuboot";
    						reg = < 0x0 0xc000 >;
    					};
    					slot0_partition: partition@c000 {
    						label = "image-0";
    						reg = < 0xc000 0x67000 >;
    					};
    					slot1_partition: partition@73000 {
    						label = "image-1";
    						reg = < 0x73000 0x67000 >;
    					};
    					scratch_partition: partition@da000 {
    						label = "image-scratch";
    						reg = < 0xda000 0x1e000 >;
    					};
    					storage_partition: partition@f8000 {
    						label = "storage";
    						reg = < 0xf8000 0x8000 >;
    					};
    				};
    			};
    		};
    		sram0: memory@20000000 {
    			compatible = "mmio-sram";
    			reg = < 0x20000000 0x40000 >;
    		};
    		adc: adc@40007000 {
    			compatible = "nordic,nrf-saadc";
    			reg = < 0x40007000 0x1000 >;
    			interrupts = < 0x7 0x1 >;
    			status = "okay";
    			label = "ADC_0";
    			#io-channel-cells = < 0x1 >;
    		};
    		clock: clock@40000000 {
    			compatible = "nordic,nrf-clock";
    			reg = < 0x40000000 0x1000 >;
    			interrupts = < 0x0 0x1 >;
    			status = "okay";
    			label = "CLOCK";
    		};
    		uart0: uart@40002000 {
    			reg = < 0x40002000 0x1000 >;
    			interrupts = < 0x2 0x1 >;
    			status = "okay";
    			label = "UART_0";
    			compatible = "nordic,nrf-uart";
    			current-speed = < 0xf4240 >;
    			tx-pin = < 0x6 >;
    			rx-pin = < 0x8 >;
    			rts-pin = < 0x1a >;
    			cts-pin = < 0x4 >;
    		};
    		uart1: arduino_serial: uart@40028000 {
    			compatible = "nordic,nrf-uarte";
    			reg = < 0x40028000 0x1000 >;
    			interrupts = < 0x28 0x1 >;
    			status = "okay";
    			label = "UART_1";
    			current-speed = < 0x1c200 >;
    			rx-pin = < 0x21 >;
    			tx-pin = < 0x22 >;
    		};
    		gpiote: gpiote@40006000 {
    			compatible = "nordic,nrf-gpiote";
    			reg = < 0x40006000 0x1000 >;
    			interrupts = < 0x6 0x5 >;
    			status = "okay";
    			label = "GPIOTE_0";
    		};
    		gpio0: gpio@50000000 {
    			compatible = "nordic,nrf-gpio";
    			gpio-controller;
    			reg = < 0x50000000 0x200 0x50000500 0x300 >;
    			#gpio-cells = < 0x2 >;
    			label = "GPIO_0";
    			status = "okay";
    			phandle = < 0x2 >;
    		};
    		gpio1: gpio@50000300 {
    			compatible = "nordic,nrf-gpio";
    			gpio-controller;
    			reg = < 0x50000300 0x200 0x50000800 0x300 >;
    			#gpio-cells = < 0x2 >;
    			ngpios = < 0x10 >;
    			label = "GPIO_1";
    			status = "okay";
    		};
    		i2c0: arduino_i2c: i2c@40003000 {
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40003000 0x1000 >;
    			clock-frequency = < 0x186a0 >;
    			interrupts = < 0x3 0x1 >;
    			status = "okay";
    			label = "I2C_0";
    			compatible = "nordic,nrf-twi";
    			sda-pin = < 0x1a >;
    			scl-pin = < 0x1b >;
    		};
    		i2c1: i2c@40004000 {
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40004000 0x1000 >;
    			clock-frequency = < 0x186a0 >;
    			interrupts = < 0x4 0x1 >;
    			status = "disabled";
    			label = "I2C_1";
    			compatible = "nordic,nrf-twi";
    			sda-pin = < 0x1e >;
    			scl-pin = < 0x1f >;
    		};
    		pwm0: pwm@4001c000 {
    			compatible = "nordic,nrf-pwm";
    			reg = < 0x4001c000 0x1000 >;
    			interrupts = < 0x1c 0x1 >;
    			status = "okay";
    			label = "PWM_0";
    			#pwm-cells = < 0x1 >;
    			ch0-pin = < 0xd >;
    			ch0-inverted;
    			phandle = < 0x3 >;
    		};
    		pwm1: pwm@40021000 {
    			compatible = "nordic,nrf-pwm";
    			reg = < 0x40021000 0x1000 >;
    			interrupts = < 0x21 0x1 >;
    			status = "disabled";
    			label = "PWM_1";
    			#pwm-cells = < 0x1 >;
    		};
    		pwm2: pwm@40022000 {
    			compatible = "nordic,nrf-pwm";
    			reg = < 0x40022000 0x1000 >;
    			interrupts = < 0x22 0x1 >;
    			status = "disabled";
    			label = "PWM_2";
    			#pwm-cells = < 0x1 >;
    		};
    		pwm3: pwm@4002d000 {
    			compatible = "nordic,nrf-pwm";
    			reg = < 0x4002d000 0x1000 >;
    			interrupts = < 0x2d 0x1 >;
    			status = "disabled";
    			label = "PWM_3";
    			#pwm-cells = < 0x1 >;
    		};
    		qdec: qdec@40012000 {
    			compatible = "nordic,nrf-qdec";
    			reg = < 0x40012000 0x1000 >;
    			interrupts = < 0x12 0x1 >;
    			status = "disabled";
    			label = "QDEC";
    		};
    		rng: random@4000d000 {
    			compatible = "nordic,nrf-rng";
    			reg = < 0x4000d000 0x1000 >;
    			interrupts = < 0xd 0x1 >;
    			status = "okay";
    			label = "RNG";
    		};
    		spi0: spi@40003000 {
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40003000 0x1000 >;
    			interrupts = < 0x3 0x1 >;
    			status = "disabled";
    			label = "SPI_0";
    			compatible = "nordic,nrf-spi";
    			sck-pin = < 0x1b >;
    			mosi-pin = < 0x1a >;
    			miso-pin = < 0x1d >;
    		};
    		spi1: spi@40004000 {
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40004000 0x1000 >;
    			interrupts = < 0x4 0x1 >;
    			status = "okay";
    			label = "SPI_1";
    			compatible = "nordic,nrf-spi";
    			sck-pin = < 0x1f >;
    			mosi-pin = < 0x1e >;
    			miso-pin = < 0x28 >;
    		};
    		spi2: spi@40023000 {
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40023000 0x1000 >;
    			interrupts = < 0x23 0x1 >;
    			status = "disabled";
    			label = "SPI_2";
    			compatible = "nordic,nrf-spi";
    			sck-pin = < 0x13 >;
    			mosi-pin = < 0x14 >;
    			miso-pin = < 0x15 >;
    		};
    		spi3: spi@4002f000 {
    			compatible = "nordic,nrf-spim";
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x4002f000 0x1000 >;
    			interrupts = < 0x2f 0x1 >;
    			status = "disabled";
    			label = "SPI_3";
    		};
    		qspi: qspi@40029000 {
    			compatible = "nordic,nrf-qspi";
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x0 >;
    			reg = < 0x40029000 0x1000 >;
    			interrupts = < 0x29 0x1 >;
    			status = "okay";
    			label = "QSPI";
    			sck-pin = < 0x13 >;
    			io-pins = < 0x14 >, < 0x15 >, < 0x16 >, < 0x17 >;
    			csn-pins = < 0x11 >;
    			mx25r64: mx25r6435f@0 {
    				compatible = "nordic,qspi-nor";
    				reg = < 0x0 >;
    				writeoc = "pp4io";
    				readoc = "read4io";
    				sck-frequency = < 0x7a1200 >;
    				label = "MX25R64";
    				jedec-id = [ C2 28 17 ];
    				size = < 0x4000000 >;
    				has-be32k;
    				has-dpd;
    				t-enter-dpd = < 0x2710 >;
    				t-exit-dpd = < 0x88b8 >;
    			};
    		};
    		rtc0: rtc@4000b000 {
    			compatible = "nordic,nrf-rtc";
    			reg = < 0x4000b000 0x1000 >;
    			interrupts = < 0xb 0x1 >;
    			status = "okay";
    			clock-frequency = < 0x8000 >;
    			prescaler = < 0x1 >;
    			label = "RTC_0";
    		};
    		rtc1: rtc@40011000 {
    			compatible = "nordic,nrf-rtc";
    			reg = < 0x40011000 0x1000 >;
    			interrupts = < 0x11 0x1 >;
    			status = "okay";
    			clock-frequency = < 0x8000 >;
    			prescaler = < 0x1 >;
    			label = "RTC_1";
    		};
    		rtc2: rtc@40024000 {
    			compatible = "nordic,nrf-rtc";
    			reg = < 0x40024000 0x1000 >;
    			interrupts = < 0x24 0x1 >;
    			status = "okay";
    			clock-frequency = < 0x8000 >;
    			prescaler = < 0x1 >;
    			label = "RTC_2";
    		};
    		timer0: timer@40008000 {
    			compatible = "nordic,nrf-timer";
    			status = "okay";
    			reg = < 0x40008000 0x1000 >;
    			interrupts = < 0x8 0x1 >;
    			prescaler = < 0x0 >;
    			label = "TIMER_0";
    		};
    		timer1: timer@40009000 {
    			compatible = "nordic,nrf-timer";
    			status = "okay";
    			reg = < 0x40009000 0x1000 >;
    			interrupts = < 0x9 0x1 >;
    			prescaler = < 0x0 >;
    			label = "TIMER_1";
    		};
    		timer2: timer@4000a000 {
    			compatible = "nordic,nrf-timer";
    			status = "okay";
    			reg = < 0x4000a000 0x1000 >;
    			interrupts = < 0xa 0x1 >;
    			prescaler = < 0x0 >;
    			label = "TIMER_2";
    		};
    		timer3: timer@4001a000 {
    			compatible = "nordic,nrf-timer";
    			status = "okay";
    			reg = < 0x4001a000 0x1000 >;
    			interrupts = < 0x1a 0x1 >;
    			prescaler = < 0x0 >;
    			label = "TIMER_3";
    		};
    		timer4: timer@4001b000 {
    			compatible = "nordic,nrf-timer";
    			status = "okay";
    			reg = < 0x4001b000 0x1000 >;
    			interrupts = < 0x1b 0x1 >;
    			prescaler = < 0x0 >;
    			label = "TIMER_4";
    		};
    		temp: temp@4000c000 {
    			compatible = "nordic,nrf-temp";
    			reg = < 0x4000c000 0x1000 >;
    			interrupts = < 0xc 0x1 >;
    			status = "okay";
    			label = "TEMP_0";
    		};
    		usbd: usbd@40027000 {
    			compatible = "nordic,nrf-usbd";
    			reg = < 0x40027000 0x1000 >;
    			interrupts = < 0x27 0x1 >;
    			num-bidir-endpoints = < 0x1 >;
    			num-in-endpoints = < 0x7 >;
    			num-out-endpoints = < 0x7 >;
    			num-isoin-endpoints = < 0x1 >;
    			num-isoout-endpoints = < 0x1 >;
    			status = "okay";
    			label = "USBD";
    		};
    		wdt: wdt0: watchdog@40010000 {
    			compatible = "nordic,nrf-watchdog";
    			reg = < 0x40010000 0x1000 >;
    			interrupts = < 0x10 0x1 >;
    			status = "okay";
    			label = "WDT";
    		};
    		cryptocell: crypto@5002a000 {
    			compatible = "nordic,nrf-cc310";
    			reg = < 0x5002a000 0x1000 >;
    			label = "CRYPTOCELL";
    			status = "okay";
    			#address-cells = < 0x1 >;
    			#size-cells = < 0x1 >;
    			cryptocell310: crypto@5002b000 {
    				compatible = "arm,cryptocell-310";
    				reg = < 0x5002b000 0x1000 >;
    				interrupts = < 0x2a 0x1 >;
    				label = "CRYPTOCELL310";
    			};
    		};
    	};
    	sw_pwm: sw-pwm {
    		compatible = "nordic,nrf-sw-pwm";
    		status = "disabled";
    		label = "SW_PWM";
    		timer-instance = < 0x2 >;
    		channel-count = < 0x3 >;
    		clock-prescaler = < 0x0 >;
    		ppi-base = < 0xe >;
    		gpiote-base = < 0x0 >;
    		#pwm-cells = < 0x1 >;
    	};
    	cpus {
    		#address-cells = < 0x1 >;
    		#size-cells = < 0x0 >;
    		cpu@0 {
    			device_type = "cpu";
    			compatible = "arm,cortex-m4f";
    			reg = < 0x0 >;
    		};
    	};
    	leds {
    		compatible = "gpio-leds";
    		led0: led_0 {
    			gpios = < &gpio0 0xd 0x1 >;
    			label = "Green LED 0";
    		};
    		led1: led_1 {
    			gpios = < &gpio0 0xe 0x1 >;
    			label = "Green LED 1";
    		};
    		led2: led_2 {
    			gpios = < &gpio0 0xf 0x1 >;
    			label = "Green LED 2";
    		};
    		led3: led_3 {
    			gpios = < &gpio0 0x10 0x1 >;
    			label = "Green LED 3";
    		};
    	};
    	pwmleds {
    		compatible = "pwm-leds";
    		pwm_led0: pwm_led_0 {
    			pwms = < &pwm0 0xd >;
    		};
    	};
    	buttons {
    		compatible = "gpio-keys";
    		button0: button_0 {
    			gpios = < &gpio0 0xb 0x11 >;
    			label = "Push button switch 0";
    		};
    		button1: button_1 {
    			gpios = < &gpio0 0xc 0x11 >;
    			label = "Push button switch 1";
    		};
    		button2: button_2 {
    			gpios = < &gpio0 0x18 0x11 >;
    			label = "Push button switch 2";
    		};
    		button3: button_3 {
    			gpios = < &gpio0 0x19 0x11 >;
    			label = "Push button switch 3";
    		};
    	};
    };
    

    Everything compiles fine.

  • Hi,

    Summing up:

    • The same firmware (except for different RTC and CTS pins) is used and works on the nRF52840 DK with the same UART-USB bridge?
    • UART communication works on your custom firmware using the same UART-USB bridge, demonstrated with e.g. functional serial DFU?
    • However, with the HCI SAMPLE you get a lot of "Frame reassembly failed", and nothing works.

    To be honest, I do not have any idea of what exactly is going on, but can you try to reduce the UART baud rate and see if that helps?

Related