This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to enable UART communication on nRF52DK_nRF52832 board?

Hi guys,

I have already asked some questions related to this topic before, but I am still trying to figure out how to enable the UART communication on my nRF52832 board. I tried multiple examples such as NordicSnippets-master/examples/uart and nRF5_SDK_17.0.0_9d13099 /examples/peripheral/uart but without success. Also, I am using Zephyr RTOS and because of that these examples were not compatible (some header files are not included, or some other functionalities are not supported in Zephyr). My main goal is to read the output in Minicom terminal when my device is not directly connected via USB to my PC, but using the FTDI cable. Can you please help me with some advice or example how can I enable the UART communication on my board because I think that is the only way to read the outputs (using Tx and Rx pins). Also, if you maybe have some other ideas how can I do that, I am open for your comments!! Thanks in advance!!

Parents
  • Hi,

    Using the example in nRF5_SDK_17.0.0_9d13099 /examples/peripheral/uart should be the simples approach. Did you test it with the virtual COM port over the USB port first, to make sure it is working with the terminal settings?

    How did you connect the FTDI cable? Did you use the default pins in the example?

    Best regards,
    Jørgen

  • Hi,

    Thanks Jorgen for the answer. I connected the FTDI cable using three pins (GND, Tx (P0.12 on the board) and Rx (P0.11 on the board), and for the power supply I used the environment emulator as the main power source. I tried with the default pins P0.06 and P0.08, but without success. I did not try to change the baudrate. Also, I found an example that works for my board, but I have a problem when I want to print out for example the voltage. I send you my code that I work on and maybe you can suggest me something. For this example, I combine the zephyr/samples/board/nrf/battery example and the code that I found on the web related to the UART communication. This is the main.c code, but also for this application I have to include the battery.c and battery.h files. I am able to print "Hello World!" via FTDI cable in my Minicom terminal, but with the voltage I have problems. Thanks in advance!!

    main.c code:

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <zephyr.h>
    #include <sys/printk.h>
    #include <drivers/uart.h>
    #include <drivers/gpio.h>
    #include "battery.h"
    
    
    #define PIN_TXD        12
    #define PIN_RXD        11
    
    
    int main(void)
    {
      uint8_t hello_world[] = "Hello World!\n";
    
      int rc = battery_measure_enable(true);
      int batt_mV = battery_sample();
      //unsigned int batt_pptt = battery_level_pptt(batt_mV, levels);
    
      // Configure the UARTE with no flow control, one parity bit and 115200 baud rate
      NRF_UARTE0->CONFIG = (UART_CONFIG_HWFC_Disabled   << UART_CONFIG_HWFC_Pos) |
                           (UART_CONFIG_PARITY_Included << UART_CONFIG_PARITY_Pos); 
      
      NRF_UARTE0->BAUDRATE = UARTE_BAUDRATE_BAUDRATE_Baud921600 << UARTE_BAUDRATE_BAUDRATE_Pos;
      
      // Select TX and RX pins
      NRF_UARTE0->PSEL.TXD = PIN_TXD;
      NRF_UARTE0->PSEL.RXD = PIN_RXD;
      
      // Enable the UART (starts using the TX/RX pins)
      NRF_UARTE0->ENABLE = UARTE_ENABLE_ENABLE_Enabled << UARTE_ENABLE_ENABLE_Pos;
      
      // Configure transmit buffer and start the transfer
      NRF_UARTE0->TXD.MAXCNT = sizeof(hello_world);
      NRF_UARTE0->TXD.PTR = (uint32_t)&hello_world[0];
      NRF_UARTE0->TXD.MAXCNT = sizeof(batt_mV);
      NRF_UARTE0->TXD.PTR = (uint32_t)&batt_mV;
      
      NRF_UARTE0->TASKS_STARTTX = 1;
     
      // Wait until the transfer is complete
      while (NRF_UARTE0->EVENTS_ENDTX == 0)
      {
      }
      
      // Stop the UART TX
      NRF_UARTE0->TASKS_STOPTX = 1;
      // Wait until we receive the stopped event
      while (NRF_UARTE0->EVENTS_TXSTOPPED == 0);
      
      // Disable the UARTE (pins are now available for other use)
      NRF_UARTE0->ENABLE = UARTE_ENABLE_ENABLE_Disabled << UARTE_ENABLE_ENABLE_Pos;
      
      while (1)
      {
        __WFE();
      }
    }

    battery.c:

    /*
     * Copyright (c) 2018-2019 Peter Bigot Consulting, LLC
     * Copyright (c) 2019-2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <zephyr.h>
    #include <init.h>
    #include <drivers/gpio.h>
    #include <drivers/adc.h>
    #include <drivers/sensor.h>
    #include <logging/log.h>
    
    #include "battery.h"
    
    LOG_MODULE_REGISTER(BATTERY, CONFIG_ADC_LOG_LEVEL);
    
    #define VBATT DT_PATH(vbatt)
    
    #ifdef CONFIG_BOARD_THINGY52_NRF52832
    /* This board uses a divider that reduces max voltage to
     * reference voltage (600 mV).
     */
    #define BATTERY_ADC_GAIN ADC_GAIN_1
    #else
    /* Other boards may use dividers that only reduce battery voltage to
     * the maximum supported by the hardware (3.6 V)
     */
    #define BATTERY_ADC_GAIN ADC_GAIN_1_6
    #endif
    
    struct io_channel_config {
    	const char *label;
    	u8_t channel;
    };
    
    struct gpio_channel_config {
    	const char *label;
    	u8_t pin;
    	u8_t flags;
    };
    
    struct divider_config {
    	struct io_channel_config io_channel;
    	struct gpio_channel_config power_gpios;
    	/* output_ohm is used as a flag value: if it is nonzero then
    	 * the battery is measured through a voltage divider;
    	 * otherwise it is assumed to be directly connected to Vdd.
    	 */
    	u32_t output_ohm;
    	u32_t full_ohm;
    };
    
    static const struct divider_config divider_config = {
    #if DT_NODE_HAS_STATUS(VBATT, okay)
    	.io_channel = {
    		DT_IO_CHANNELS_LABEL(VBATT),
    		DT_IO_CHANNELS_INPUT(VBATT),
    	},
    #if DT_NODE_HAS_PROP(VBATT, power_gpios)
    	.power_gpios = {
    		DT_GPIO_LABEL(VBATT, power_gpios),
    		DT_GPIO_PIN(VBATT, power_gpios),
    		DT_GPIO_FLAGS(VBATT, power_gpios),
    	},
    #endif
    	.output_ohm = DT_PROP(VBATT, output_ohms),
    	.full_ohm = DT_PROP(VBATT, full_ohms),
    #else /* /vbatt exists */
    	.io_channel = {
    		DT_LABEL(DT_ALIAS(adc_0)),
    	},
    #endif /* /vbatt exists */
    };
    
    struct divider_data {
    	struct device *adc;
    	struct device *gpio;
    	struct adc_channel_cfg adc_cfg;
    	struct adc_sequence adc_seq;
    	s16_t raw;
    };
    static struct divider_data divider_data;
    
    static int divider_setup(void)
    {
    	const struct divider_config *cfg = &divider_config;
    	const struct io_channel_config *iocp = &cfg->io_channel;
    	const struct gpio_channel_config *gcp = &cfg->power_gpios;
    	struct divider_data *ddp = &divider_data;
    	struct adc_sequence *asp = &ddp->adc_seq;
    	struct adc_channel_cfg *accp = &ddp->adc_cfg;
    	int rc;
    
    	if (iocp->label == NULL) {
    		return -ENOTSUP;
    	}
    
    	ddp->adc = device_get_binding(iocp->label);
    	if (ddp->adc == NULL) {
    		LOG_ERR("Failed to get ADC %s", iocp->label);
    		return -ENOENT;
    	}
    
    	if (gcp->label) {
    		ddp->gpio = device_get_binding(gcp->label);
    		if (ddp->gpio == NULL) {
    			LOG_ERR("Failed to get GPIO %s", gcp->label);
    			return -ENOENT;
    		}
    		rc = gpio_pin_configure(ddp->gpio, gcp->pin,
    					GPIO_OUTPUT_INACTIVE | gcp->flags);
    		if (rc != 0) {
    			LOG_ERR("Failed to control feed %s.%u: %d",
    				gcp->label, gcp->pin, rc);
    			return rc;
    		}
    	}
    
    	*asp = (struct adc_sequence){
    		.channels = BIT(0),
    		.buffer = &ddp->raw,
    		.buffer_size = sizeof(ddp->raw),
    		.oversampling = 4,
    		.calibrate = true,
    	};
    
    #ifdef CONFIG_ADC_NRFX_SAADC
    	*accp = (struct adc_channel_cfg){
    		.gain = BATTERY_ADC_GAIN,
    		.reference = ADC_REF_INTERNAL,
    		.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
    	};
    
    	if (cfg->output_ohm != 0) {
    		accp->input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0
    			+ iocp->channel;
    	} else {
    		accp->input_positive = SAADC_CH_PSELP_PSELP_VDD;
    	}
    
    	asp->resolution = 14;
    #else /* CONFIG_ADC_var */
    #error Unsupported ADC
    #endif /* CONFIG_ADC_var */
    
    	rc = adc_channel_setup(ddp->adc, accp);
    	LOG_INF("Setup AIN%u got %d", iocp->channel, rc);
    
    	return rc;
    }
    
    static bool battery_ok;
    
    static int battery_setup(struct device *arg)
    {
    	int rc = divider_setup();
    
    	battery_ok = (rc == 0);
    	LOG_INF("Battery setup: %d %d", rc, battery_ok);
    	return rc;
    }
    
    SYS_INIT(battery_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
    
    int battery_measure_enable(bool enable)
    {
    	int rc = -ENOENT;
    
    	if (battery_ok) {
    		const struct divider_data *ddp = &divider_data;
    		const struct gpio_channel_config *gcp = &divider_config.power_gpios;
    
    		rc = 0;
    		if (ddp->gpio) {
    			rc = gpio_pin_set(ddp->gpio, gcp->pin, enable);
    		}
    	}
    	return rc;
    }
    
    int battery_sample(void)
    {
    	int rc = -ENOENT;
    
    	if (battery_ok) {
    		struct divider_data *ddp = &divider_data;
    		const struct divider_config *dcp = &divider_config;
    		struct adc_sequence *sp = &ddp->adc_seq;
    
    		rc = adc_read(ddp->adc, sp);
    		sp->calibrate = false;
    		if (rc == 0) {
    			s32_t val = ddp->raw;
    
    			adc_raw_to_millivolts(adc_ref_internal(ddp->adc),
    					      ddp->adc_cfg.gain,
    					      sp->resolution,
    					      &val);
    
    			if (dcp->output_ohm != 0) {
    				rc = val * (u64_t)dcp->full_ohm
    					/ dcp->output_ohm;
    				LOG_INF("raw %u ~ %u mV => %d mV\n",
    					ddp->raw, val, rc);
    			} else {
    				rc = val;
    				LOG_INF("raw %u ~ %u mV\n", ddp->raw, val);
    			}
    		}
    	}
    
    	return rc;
    }
    
    unsigned int battery_level_pptt(unsigned int batt_mV,
    				const struct battery_level_point *curve)
    {
    	const struct battery_level_point *pb = curve;
    
    	if (batt_mV >= pb->lvl_mV) {
    		/* Measured voltage above highest point, cap at maximum. */
    		return pb->lvl_pptt;
    	}
    	/* Go down to the last point at or below the measured voltage. */
    	while ((pb->lvl_pptt > 0)
    	       && (batt_mV < pb->lvl_mV)) {
    		++pb;
    	}
    	if (batt_mV < pb->lvl_mV) {
    		/* Below lowest point, cap at minimum */
    		return pb->lvl_pptt;
    	}
    
    	/* Linear interpolation between below and above points. */
    	const struct battery_level_point *pa = pb - 1;
    
    	return pb->lvl_pptt
    	       + ((pa->lvl_pptt - pb->lvl_pptt)
    		  * (batt_mV - pb->lvl_mV)
    		  / (pa->lvl_mV - pb->lvl_mV));
    }

    battery.h:

    /*
     * Copyright (c) 2018-2019 Peter Bigot Consulting, LLC
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #ifndef APPLICATION_BATTERY_H_
    #define APPLICATION_BATTERY_H_
    
    /** Enable or disable measurement of the battery voltage.
     *
     * @param enable true to enable, false to disable
     *
     * @return zero on success, or a negative error code.
     */
    int battery_measure_enable(bool enable);
    
    /** Measure the battery voltage.
     *
     * @return the battery voltage in millivolts, or a negative error
     * code.
     */
    int battery_sample(void);
    
    /** A point in a battery discharge curve sequence.
     *
     * A discharge curve is defined as a sequence of these points, where
     * the first point has #lvl_pptt set to 10000 and the last point has
     * #lvl_pptt set to zero.  Both #lvl_pptt and #lvl_mV should be
     * monotonic decreasing within the sequence.
     */
    struct battery_level_point {
    	/** Remaining life at #lvl_mV. */
    	u16_t lvl_pptt;
    
    	/** Battery voltage at #lvl_pptt remaining life. */
    	u16_t lvl_mV;
    };
    
    /** Calculate the estimated battery level based on a measured voltage.
     *
     * @param batt_mV a measured battery voltage level.
     *
     * @param curve the discharge curve for the type of battery installed
     * on the system.
     *
     * @return the estimated remaining capacity in parts per ten
     * thousand.
     */
    unsigned int battery_level_pptt(unsigned int batt_mV,
    				const struct battery_level_point *curve);
    
    #endif /* APPLICATION_BATTERY_H_ */

  • Are you having troubles with getting the voltage from the ADC, or with sending the voltage over UART?

    It looks like you are setting the two buffers right after each other. This will only overwrite the buffers, not set both. The UARTE peripheral supports double-buffering of the buffer pointers and sizes, but you need to trigger the STARTTX/RX task and wait for the TX/RXSTARTED event between setting the pointers.

Reply Children
No Data
Related