Skip to content

UART's and ADC's

tabemann edited this page Dec 22, 2022 · 6 revisions

UART's

full and full_swdcom builds of zeptoforth come with built-in support for the UART's on each of the supported platforms with the sole exception of LPUART1 on the STM32L476, which is not currently supported. This support allows reading and writing of bytes from said UART's along with checking whether bytes are ready to read or whether buffer space is available to write bytes. It also allows changing the baud rate of all the supported UART's from the default of 115200 baud.

Note that the UART's on a given platform need not be numbered in any particular fashion. For instance, on the RP2040 the UART's are numbered 0 (the console) and 1, whereas on the STM32F411 the UART's are numbered 1, 2 (the console), and 6. On the STM32L476 the UART's (ignoring LPUART1) are numbered 1 through 5, with 2 being the console. On the STM32F407 the UART's are numbered 1 through 6, with 2 being the console. On the STM32F746 the UART's are numbered 1 through 8, with 1 being the console.

For an example of using a UART on the Raspberry Pi Pico, consider the following. Here two terminal emulators set to 115200 baud are connected to two different USB-serial dongles, one of which is connected to pins GPIO0 (UART0 TX) and GPIO1 (UART0 RX), and one of which is connected to GPIO4 (UART1 TX) and GPIO5 (UART1 RX):

uart import
pin import
1 4 uart-pin
1 5 uart-pin
: test-type ( c-addr u -- ) swap tuck + swap ?do i c@ 1 >uart loop ;
s" FOOBAR" test-type

Here we configure GPIO4 and GPIO5 to be pins for UART1 with 1 4 uart-pin and 1 5 uart-pin respectively. Then we write six bytes FOOBAR, to UART1, which then appear on the terminal emulator.

For a similar example, but involving changing the baud, use the same setup, except with the terminal emulator set to 9600 baud.

uart import                                                         
pin import                                                          
9600 1 uart-baud!
1 4 uart-pin                                                        
1 5 uart-pin                                                        
: test-type ( c-addr u -- ) swap tuck + swap ?do i c@ 1 >uart loop ;
s" QUUX" test-type

First we set UART1 to 9600 baud with 9600 1 uart-baud!. Then we configure GPIO4 and GPIO5 to be pins for UART1. Then then write four bytes, QUUX, to USART1, which then appear on the terminal emulator.

For an example of reading successive characters off a serial connection, consider the following:

uart import
pin import
1 4 uart-pin
1 5 uart-pin
: test-read ( -- ) begin 1 uart>? if 1 uart> emit then key? until key drop ;

Afterwards, execute:

test-read FOOBAR ok

After configuring GPIO4 and GPIO5 we in a loop check whether bytes are available to be read on UART1; when they are, we read them and emit them to the console (swdcom in this case). Once a key is pressed on the console, we exit out of the loop and then dispose of the key that was read off the console. In this case we entered FOOBAR at the terminal emulator (note this will not echo anything on the terminal emulator) and this was printed on the console.

For an example of using a UART on an STM32 platform, take the following example on the STM32F411. Here a terminal emulator set to 115200 baud is connected to a USB-serial dongle which in turn is connected to pins PB6 (TX1) and PB7 (RX1) on an STM32F411 "Black Pill" board, and ST-Link is attached to the SWD pins of the board to enable the use of swdcom:

uart import
pin import
1 6 xb uart-pin
1 7 xb uart-pin
: test-type ( c-addr u -- ) swap tuck + swap ?do i c@ 1 >uart loop ;
s" FOOBAR" test-type

Here we configure PB6 and PB7 to be pins for USART1, using STM terminology, with 1 6 xb uart-pin and 1 7 xb uart-pin respectively. Then then write six bytes, FOOBAR, to USART1, which then appear on the terminal emulator.

For a similar example, but involving changing the baud, use the same setup, except with the terminal emulator set to 9600 baud.

uart import
pin import
9600 1 uart-baud!
1 6 xb uart-pin
1 7 xb uart-pin
: test-type ( c-addr u -- ) swap tuck + swap ?do i c@ 1 >uart loop ;
s" QUUX" test-type

First we set USART1 to 9600 baud with 9600 1 uart-baud!. Then we configure PB6 and PB7 to be pins for USART1. Then then write four bytes, QUUX, to USART1, which then appear on the terminal emulator.

For an example of reading successive characters off a serial connection, consider the following:

uart import
pin import
1 6 xb uart-pin
1 7 xb uart-pin
: test-read ( -- ) begin 1 uart>? if 1 uart> emit then key? until key drop ;

Afterwards, execute:

test-read FOOBAR ok

After configuring PB6 and PB7 we in a loop check whether bytes are available to be read on USART1; when they are, we read them and emit them to the console (swdcom in this case). Once a key is pressed on the console, we exit out of the loop and then dispose of the key that was read off the console. In this case we entered FOOBAR at the terminal emulator (note this will not echo anything on the terminal emulator) and this was printed on the console.

Analog-to-Digital Converters

Analog-to-digital converters (ADC's) are supported on all of the supported platforms. This includes support for built-in internal temperature sensor, Vrefint voltage, and Vbat voltage ADC channels, even though on some of the supported platforms these are not all usable simultaneously. Note that all of the supported platforms make use of 12-bit ADC results, even if the underlying hardware optionally supports less precision.

STM32F407, STM32F746, and STM32L476 microcontrollers provide three ADC's whereas RP2040 and STM32F411 microcontrollers only provide one ADC. STM32F407, STM32F746, and STM32L476 microcontrollers' ADC's are numbered 1 through 3, whereas the RP2040's ADC is numbered 0 and the STM32F411's ADC is numbered 1. default-adc is 1 on all platforms except for the RP2040, where it is 0. All internal ADC channels on the supported platforms are on default-adc.

Take the following example of reading the internal temperature sensor ADC channel from a Raspberry Pi Pico:

adc import
: test ( -- ) 10 0 do temp-adc-chan default-adc adc@ . 1000 ms loop ;

Afterwards, execute:

test 889 885 885 885 886 885 886 885 885 885  ok

Here we read the ADC channel temp-adc-chan on default-adc (which is 0 on the RP2040) ten times and display the results. Converting these to temperatures is an exercise left to the reader.

Take the following example of reading the internal temperature sensor ADC channel from an STM32F411:

adc import
480 temp-adc-chan default-adc adc-sampling-time!
: test ( -- ) 10 0 do temp-adc-chan default-adc adc@ . 1000 ms loop ;

Afterwards, execute:

test 964 962 961 961 961 962 962 961 961 960  ok

Here we first set the sampling time for the ADC channel temp-adc-chan on default-adc (which is 1 on the STM32F411) to 480 * 1/ADCCLK, which is the maximum sampling time on the STM32F411 (as is also the case on the STM32F407 and STM32F746), because unless a high sampling time is used, the initial internal temperature sensor value will be erroneous on STM32 platforms. (On the STM32L476 the maximum value is 640 * 1/ADCCLK + 0.5.) Note that when a sampling time is specified, it is rounded up to the next larger valid sampling time. Then we read temp-adc-chan on default-adc ten times and display the results. Likewise, converting these to temperatures is an exercise left to the reader.