It is time to use some alternate functions on ports pins. For this example we will use alternate function of PORTA.2 and PORTA.3 to serve as RX/TX of USART2.
We start new project for Nucleo F401RE in CubeMX:
Now we need to configure USART2:
On the left side select Asynchronous from the drop-down menu of USART2 (marked red).
In the middle you will notice change of color of PINA.2 and PINA.3 (marked green).
Now let’s generate project and open it in SW4STM32.
There is not much of a code added for USART:
- command to initialize USART2:
1 |
MX_USART2_UART_Init(); |
- and the initialization code itself:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } |
Also there is initialization of ports and pins:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : B1_Pin */ GPIO_InitStruct.Pin = B1_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : LD2_Pin */ GPIO_InitStruct.Pin = LD2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct); } |
But wait… there is no alternate function selection here or any mention of pins PINA.2 and PINA.3… This is done in function: HAL_UART_MspInit(UART_HandleTypeDef* huart) inside stm32f4xx_hal_msp.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct; if(huart->Instance==USART2) { /* USER CODE BEGIN USART2_MspInit 0 */ /* USER CODE END USART2_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN USART2_MspInit 1 */ /* USER CODE END USART2_MspInit 1 */ } } |
To check if alternate bits really are set, we need to debug our program and check values of register in I/O Registers window.
First I will add a small delay to set breakpoint at:
1 2 3 4 5 |
/* USER CODE BEGIN 3 */ HAL_Delay(10); } /* USER CODE END 3 */ |
and set breakpoint (right click on the scroll bar left from line numbers, where you want to add breakpoint and select Toggle breakpoint):
this will add little blue circle at the breakpoint (circled in red):
Now we need to start the debug, we do this by clicking “little green bug” in toolbar (marked red here):
This opens so called Debug perspective in our SW4STM32. You should see something like picture below. If you clicked something and windows changed, don’t panic, just go to: Window -> Perspective -> Reset Perspective… and all will be reset to default view.
Now click on “play” marked green on the screenshot above and program will start and pause at breakpoint. Now double click on I/O Registers and it will open the registers windows full-screen.
Now click through menu to: GPIO -> GPIOA and expand AFRL and double click on it so that it fetches values from microcontroller:
Here you can see values for AFRL which shows that AFRL2 and AFRL3 have values of 0x7.
Now if you check alternate functions of PORTA pins you can see that these mean that pins’ PINA.2 and PINA.3 alternate functions are set as USART RX/TX:
Now that we checked that everything is set, we need to send some data over UART.
We already have serial communication configured. We only need to add a few commands for sending data. HAL command for this is HAL_UART_Transmit.
This two commands will send “Hello world!” over uart and also send “new line” command. We do this every 5 seconds.
1 2 3 4 5 |
/* USER CODE BEGIN 3 */ HAL_UART_Transmit(&huart2, (uint8_t *) "Hello world!\n", 13, 100); HAL_Delay(5000); } /* USER CODE END 3 */ |
Now that this works we can do second part of this tutorial -> send unsigned long number over UART. One thing we need to understand is that if we send for example number 5 over uart it will not send number 5, it will send number 53 which is ascii representation of number 5 (see table below). Everything we send over uart gets transformed this way.
All serial communication we will be using uses 8bit numbers which means that we cannot just send unsigned long over uart, but we need to somehow change it to numbers and send ecah number separately over uart. First we need to determine maximum length of string which represents the biggest unsigned long number and that number is 2^32 – 1 = 4,294,967,295. Here we can see that we need to send 10 numbers for maximum value. There are two ways to change number to ascii string, one is using atoi standard C function and the second is to just write some function which will do this.
I must say that I am not sure why but original function from C doesn’t work ok for me, depending on level of optimiztion in SW4STM32 I get different results. My function works for what I need it, but as my friend TILZ0R from STM32F4 discovery.net says, each function must be robust and I am not sure if mine is robust enough. Enough talking, let’s start coding!
Here is my test program for sending unsigned long over uart:
first we need a couple of variables:
1 2 3 4 5 6 |
int main(void) { /* USER CODE BEGIN 1 */ unsigned long number; char buffer[10]; /* USER CODE END 1 */ |
we also need to define our function:
1 2 3 4 5 |
/* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ void GS_TX_long(unsigned long txl_number, UART_HandleTypeDef *huart); /* USER CODE END PFP */ |
we also need function not just it’s definition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* USER CODE BEGIN 4 */ void GS_TX_long(unsigned long txl_number, UART_HandleTypeDef *huart) { unsigned char txl_value[8], txl_i; unsigned long txl_tmp; txl_i = 8; while(txl_i-- > 0) { txl_tmp = txl_number % 16; //calculate remainder of division by 16 txl_number /= 16; //divide number by 16 if(txl_tmp > 9) //check if remainder of division is greater than 9 txl_value[txl_i] = txl_tmp + 55; //change numbers 10..15 to A..F else txl_value[txl_i] = txl_tmp + '0'; //change numbers 0..9 to 48..57 add 48 } HAL_UART_Transmit(&huart2, (uint8_t *) "0x", 2, 100); //transmit 0x before number HAL_UART_Transmit(&huart2, (uint8_t *) txl_value, 8, 100); //transmit buffer for uns. long number HAL_UART_Transmit(&huart2, (uint8_t *) "\n", 1, 100); //transmit new line for easier reading of result } /* USER CODE END 4 */ |
we also need to tell our compiler to include standard C library:
1 2 3 |
/* USER CODE BEGIN Includes */ #include <stdlib.h> /* USER CODE END Includes */ |
and finally we use our nice function together with itoa function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* USER CODE BEGIN 3 */ HAL_UART_Transmit(&huart2, (uint8_t *) "itoa", 4, 100); HAL_UART_Transmit(&huart2, (uint8_t *) "\n", 1, 100); number = GPIOA->AFR[1]; itoa (number, buffer, 16); HAL_UART_Transmit(&huart2, (uint8_t *) buffer, 8, 100); number = GPIOA->AFR[0]; itoa (number, buffer, 16); HAL_UART_Transmit(&huart2, (uint8_t *) buffer, 8, 100); HAL_UART_Transmit(&huart2, (uint8_t *) "\n", 1, 100); HAL_UART_Transmit(&huart2, (uint8_t *) "GS_TX_long", 10, 100); HAL_UART_Transmit(&huart2, (uint8_t *) "\n", 1, 100); GS_TX_long(GPIOA->AFR[1], &huart2); GS_TX_long(GPIOA->AFR[0], &huart2); HAL_UART_Transmit(&huart2, (uint8_t *) "\n", 1, 100); while(1); } /* USER CODE END 3 */ |
Here is result of my test, yours might be different:
First line is just to show that next line uses function itoa.
Second line is result of transfering number with itoa function (if someone knows what is the problem, please let me know).
Third line says that next two lines use my function (GS_TX_long) and following two lines show values from GPIOA->AFRLH and GPIOA-AFRL.
This concludes this tutorial. Thank you for visiting my site!