cancel
Showing results for 
Search instead for 
Did you mean: 

USART6 bare metal setup correct?I am not getting output.

CMcC.1
Associate II

Hi there.

I have been trying to configure this USART6 on my STM32F401RE board as 9600 and Async.

But I just can't seem to figure out what I am doing wrong? I am not using the HAL for this project.

I really just want to get an example of how to use the UART6 on PortA with this MCU.

Thanks

#define STM32F401xx
#include "stm32f4xx.h"
 
void USART_write(int ch);
void delayMs(int delay);
void USART6_Init();
 
int main(void)
{
	USART6_Init();
	GPIOA->MODER |= 0x400;
	//GPIOA->ODR |= 0x20;
	while(1){
		USART_write("H");
		USART_write("E");
		USART_write("L");
		USART_write("L");
		USART_write("O");
		delayMs(10);
		GPIOA->ODR ^= 0x20;
	}
}
 
void USART6_Init(){
	//RCC->APB2RSTR |= (1 << 5);
	//delayMs(1);
	//RCC->APB2RSTR |= (0 << 5);
	RCC->AHB1ENR |= 1;
	RCC->APB2ENR |= (1 << 5);
	GPIOA->AFR[0] |= (1<<15);
	GPIOA->MODER |= ( 1 << 23 );
	GPIOA->MODER |= ( 1 << 25 );
	USART6->BRR = 0x0683; //BAUDRATE 9600 @16Mhz
	USART6->CR1 = 0x0008; //ENABLE Tx
	USART6->CR1 |= 0x2000;
}
 
void USART_write(int ch){
	//WHile the TX Buffer is empty
	while(!(USART6->SR & 0x0080)){
		USART6->DR = (ch & 0xFF);
	}
}
 
void delayMs(int delay) {
	int i;
	for (; delay>0;delay--){
		for (i = 0 ; i < 3195; i++);
	}
}

11 REPLIES 11
TDK
Guru

What happens when you debug it? Does it step through everything okay or does it get stuck within a particular loop?

Have you looked at the signal on the TX line?

It's a lot easier to read code that uses the defines in the CMSIS header files. For example "USART6->CR1 = USART_CR1_TE" instead of "USART6->CR1 = 0x0008".

If you feel a post has answered your question, please click "Accept as Solution".
Guenael Cadier
ST Employee

After your initialisation step, I expect that TXE bit is SR is 1 (TX empty => ready to transmit new data), and then when you call USART_write() function, the while condition is false : if so, no writing in DR is performed.

Could you check that you execute properly the USART6->DR = xx; when debugging step by step ?

Regards

Guenael

berendi
Principal
while(!(USART6->SR & 0x0080)){
}
USART6->DR = (ch & 0xFF);

As @Guenael Cadier​ has noted, it would write the data register while TXE == 0, but the data should rather be written when TXE is no longer 0.

while(!(USART6->SR & 0x0080)){
}
USART6->DR = (ch & 0xFF);

CMcC.1
Associate II

Hi everyone

Thanks allot for the quick reply. Ok so I changed my code to send While TXE != 0 and it enters the Write function now when I step.

Problem is my USB2Serial converter has a little orange TX light indicator. BUt that never flickers or does anything when I repeatedly send one char.

SO I though I might be missing something in therms of PINOUT.

I am using these two pins on the board in the RED triangle. Those are the RX and TX pin as I have set it up right?

I believe only the PA11 pin is capable of UART6 TX?

0693W000001pWkHQAU.jpg

CMcC.1
Associate II

Ok so while I was busy cutting and pasting my docs to put up here I notice that I am writing the special function register LOW and because PINA11 and 12 is on HIGH its the wrong register to write it into. So I changed line 30 from

GPIOA->AFR[0] |= (1<<15);

to

GPIOA->AFR[1] |= (1<<15);

So now I see my TX flashing and I have some output on my terminal although its not good output. The output is gibberish.

If I run a 16Mhz clock and I want a 9600 baud rate then the settings I have should work right?

  1. USART6->BRR = 0x0683; //BAUDRATE 9600 @16Mhz
  2. USART6->CR1 = 0x0008; //ENABLE Tx
  3. USART6->CR1 |= 0x2000;

0693W000001pWsLQAU.png

0693W000001pWsaQAE.png

Some investigation ideas ...

0x0683 seems ok for me in BRR register for getting a 9600 baudrate based on a 16Mhz clock.

Default setting in CR1 CR2 also imply : 1 stop bit, No parity.

If your terminal is configured accordingly, could you capture (scope) a data being sent and measure the transmission time of 1 bit, and then check if it is corresponding to expected 9600, or something else ?

If not 9600, maybe the 16Mhz is not the freq available on APB2 ?

Regards

CMcC.1
Associate II

So I have been trying to do what I can without a scope. I wrote a simple C++ UART port term.

If I am not fooling myself I hope that the bits I am reading out is actually the right thing.

So here is my code that my STM board is running on. It simply sends 0b00000001 and then increments it until it reaches 127 and then restarts.

I see something interesting that I can't explain. It seems like I receive exactly these bytes as it increments but its still not a legitimate char?

Another weird thing is if I uncomment the additional USART_Write(); in the while loop it simply isn't sent over.

This is the output that you can actually see increment.

https://pastebin.com/52Wud7VT

/**
  ******************************************************************************
  * @file    main.c
  * @author  Auto-generated by STM32CubeIDE
  * @version V1.0
  * @brief   Default main function.
  ******************************************************************************
*/
 
 
//APB1 : UART2
//UASRT Connected to PA2
 
#define STM32F401xx
#include "stm32f4xx.h"
 
void USART_write(int ch);
void delayMs(int delay);
void USART6_Init();
 
int main(void)
{
	USART6_Init();
	GPIOA->MODER |= 0x400;
	//GPIOA->ODR |= 0x20;
	char xx = 0b00000001;
	char x[7] = {0};
	x[0] = 0b1001000;
	x[1] = 0b1000101;
	x[2] = 0b1001100;
	x[3] = 0b1001100;
	x[4] = 0b1101111;
 
	while(1){
		USART_write(xx);
		//USART_write(x[0]);
		//USART_write(x[1]);
		//USART_write(x[2]);
		//USART_write(x[3]);
		//USART_write(x[4]);
 
		delayMs(1000);
		xx++;
		if (xx == 127) {
			xx = 1;
		}
		GPIOA->ODR ^= 0x20;
	}
}
void USART6_Init(){
	//delayMs(1);
	RCC->AHB1ENR |= 1;
	RCC->APB2ENR |= (1 << 5);
	GPIOA->AFR[1] |= (1<<15);
	GPIOA->MODER |= ( 1 << 23 );
	GPIOA->MODER |= ( 1 << 25 );
 
	USART6->CR1 &= ~(1 << 12);      //  USART_WORDLENGTH_8B
	USART6->CR1 &= ~(1 << 10);      //  USART_PARITY_NONE
	USART6->CR1 |=  (1 <<  7);      //  USART_CR1_TXEIE
	USART6->CR1 |=  (1 <<  5);      //  USART_CR1_RXNEIE
	USART6->CR1 |=  (1 <<  3);      //  USART_MODE_TX
	USART6->CR1 |=  (1 <<  2);      //  USART_MODE_RX
 
 
	//USART6->BRR = 0x0683; //BAUDRATE 9600 @16Mhz//11010000011
	USART6->BRR = 0b11010000011;
	//USART6->BRR = 0x2D9;
	//208.375
	//104.1875
	USART6->CR1 = 0x0008; //ENABLE Tx
	USART6->CR1 |= 0x2000;
}
 
void USART_write(int ch){
	//WHile the TX Buffer is empty
	while((USART6->SR & 0x0080)){
		USART6->DR = (ch & 0xFF);
	}
}
 
void delayMs(int delay) {
	int i;
	for (; delay>0;delay--){
		for (i = 0 ; i < 3195; i++);
	}
}

This is a little snippit of whats in the pastebin

Leng: 1 rate :13 Char:
0x00000000000000000000000000000010
Leng: 1 rate :13 Char:
0x00000000000000000000000000000011
Leng: 1 rate :13 Char:
0x00000000000000000000000000000100
Leng: 1 rate :13 Char:
0x00000000000000000000000000000101
Leng: 1 rate :13 Char:
0x00000000000000000000000000000110
Leng: 1 rate :13 Char:
0x00000000000000000000000000000111
Leng: 1 rate :13 Char:
0x00000000000000000000000000001000
Leng: 1 rate :13 Char:
0x00000000000000000000000000001001
Leng: 1 rate :13 Char:
 
0x00000000000000000000000000001010
Leng: 1 rate :13 Char:
 
0x00000000000000000000000000001011
Leng: 1 rate :13 Char:

If anybody is interested in my C++ UART implementation here it is

#include <iostream>
#include <unistd.h>			//Used for UART
#include <fcntl.h>			//Used for UART
#include <termios.h>			//Used for UART
#include <assert.h>
#include <string.h>
#include <vector>
#include <thread>
 
using namespace std;
 
void doUART(int BR, int i);
void printb(unsigned int v);
 
int main(int argc, char **argv)
{
	
	std::vector<int> Baudrates;
	for (int x= 1 ; x < 18 ; x++) {
		Baudrates.push_back(x);
	}
	
	for (int x = 1; x < 18; x++) {
		Baudrates.push_back(x + 10000);
	}
 
/*
	for (int x = 0 ; x < Baudrates.size(); x++) {
		doUART(Baudrates[x]);
	}*/
	doUART(B9600, 0);
}
 
void printb(unsigned int v)
{
	unsigned int i, s = 1 << ((sizeof(v) << 3) - 1); // s = only most significant bit at 1
	printf("0x");
	for (i = s; i; i >>= 1)
	{
		printf("%d", v & i || 0);
	}
	printf("\n");
}
 
void doUART(int BR, int i){
	//-------------------------
	//----- SETUP USART 0 -----
	//-------------------------
	//At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
	int uart0_filestream = -1;
 
	//OPEN THE UART
	//The flags (defined in fcntl.h):
	//	Access modes (use 1 of these):
	//		O_RDONLY - Open for reading only.
	//		O_RDWR - Open for reading and writing.
	//		O_WRONLY - Open for writing only.
	//
	//	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
	//											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
	//											immediately with a failure status if the output can't be written immediately.
	//
	//	O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
	uart0_filestream = open("/dev/ttyACM1", O_RDONLY | O_NOCTTY); //| O_NDELAY);		//Open in non blocking read/write mode
	if (uart0_filestream == -1)
	{
		//ERROR - CAN'T OPEN SERIAL PORT
		printf("Error - Unable to open UART.  Ensure it is not in use by another application\n");
	}
 
	//CONFIGURE THE UART
	//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
	//	Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
	//	CSIZE:- CS5, CS6, CS7, CS8
	//	CLOCAL - Ignore modem status lines
	//	CREAD - Enable receiver
	//	IGNPAR = Ignore characters with parity errors
	//	ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
	//	PARENB - Parity enable
	//	PARODD - Odd parity (else even)
	struct termios options;
	tcgetattr(uart0_filestream, &options);
	//options.c_cflag = B9600 | CS8 | PARENB | CLOCAL | CREAD; //<Set baud rate
	options.c_cflag = BR | CS8 | PARENB | CLOCAL | CREAD; //<Set baud rate
	//options.c_iflag = INPCK;
	//options.c_cflag |= CS8|PARENB; cfsetispeed(&options, B9600); cfsetospeed(&options, B9600);
	//options.c_cflag &= ~(CRTSCTS|CSTOPB|CSIZE);
	//options.c_oflag = 0;
	//options.c_lflag = 0;
	tcflush(uart0_filestream, TCIFLUSH);
	tcsetattr(uart0_filestream, TCSANOW, &options);
 
	// Read up to 255 characters from the port if they are there
	unsigned char rx_buffer[256] = {0};
	int rx_length;
	unsigned int num_of_data_errors = 0;
 
	rx_length = read(uart0_filestream, (void *)rx_buffer, 255);
	rx_length = 0; // eliminate the first imcomplete received data message
 
	auto begin = std::chrono::high_resolution_clock::now();
	//std::cout << elapsed.count() << " ms elapsed.\n";
 
	//----- CHECK FOR ANY RX BYTES -----
	do
	{
		
		if (rx_length < 0)
		{
			perror("The following error occurred");
			//cout << "An error occured (will occur if there are no bytes)" << endl;
		}
		else if (rx_length == 0)
		{
			perror("The following error occurred");
			//cout << "No data waiting" << endl;
		}
		else
		{
			//Bytes received
			//rx_buffer[rx_length] = '\0';
			//cout << "rx_length = " << rx_length << "\trx_buffer = " << rx_buffer;
			cout << "Leng: " << rx_length << " rate :" << BR << " Char: " << rx_buffer << std::endl;
			printb(rx_buffer[0]);
			// printb(rx_buffer[1]);
			// printb(rx_buffer[2]);
			// printb(rx_buffer[3]);
			// printb(rx_buffer[4]);
			// printb(rx_buffer[5]);
			// printb(rx_buffer[6]);
			// printb(rx_buffer[7]);
			// printb(rx_buffer[8]);
			// printb(rx_buffer[9]);
			// printb(rx_buffer[10]);
			// printb(rx_buffer[11]);
			// printb(rx_buffer[12]);
			// printb(rx_buffer[13]);
			//assert(strcmp((const char*)rx_buffer, "HELLO_WORLD!! \r\n")==0);
		}
 
		if (i == 1) {
			auto end = std::chrono::high_resolution_clock::now();
			std::chrono::duration<double, std::milli> elapsed = end - begin;
			//std::cout << "ELAPSED:" << elapsed.count() << std::endl;
			if (elapsed.count() > 1000)
			{
				break;
			}
		}
 
	} while (rx_length = read(uart0_filestream, (void *)rx_buffer, 255)); //Filestream, buffer to store in, number of bytes to read (max));
 
	//----- CLOSE THE UART -----
	close(uart0_filestream);
}

So I got legitimate characters from it at least with my method I explained below. With the method I also get this string in Terminal Programs like CU for LINUX.

▒▒!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}

No I am thinking that my current implementation of USART_Write() is faulty.

When I restart the CU Terminal the characters are messed up again.

TDK
Guru

> It seems like I receive exactly these bytes as it increments but its still not a legitimate char?

ASCII codes below 32 are not printable characters. Seems like the code is working fine. When it hits 32, characters are output.

If you feel a post has answered your question, please click "Accept as Solution".