1
\$\begingroup\$

I am using the UART of an STM32F103C8T6. When selecting the Asynchronous mode via the CubeMX everything works fine using either of the functions HAL_UART_Transmit_IT() and HAL_UART_Transmit(). I am using a Baud Rate of 250kBits/s (for DMX).

When I set the mode to LIN and don't change anything else, the baud rate which the STM is sending at isn't correct anymore (measured with an oscilloscope), even though I didn't change it in my configuration.

Interestingly when I set the baud rate to (the wrong value of) ~230kBits/s it sends at approximately the correct data rate again. This is when using HAL_UART_Transmit_IT(). When using HAL_UART_Transmit(), I need to set it to ~210kBits/s to achieve a decently correct baud rate.

What could be the cause of this problem? Just changing it to a wrong value such that it somehow kinda works without knowing what's going on is a bit unsettling and isn't what I'd consider a great solution. Also, the receiver doesn't really decode the data correctly.

Thanks for your help.

The code:

main.c (in Asynchronous mode):

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t DMX_state = 0; // 0: idle (ready to send), 1: data is available, 2: break on DMX line, 3: mark after break on DMX line, 4: sending data
uint8_t DMX_loop_counter = 0;
uint8_t DMX_data[513]; // first byte is the zero-byte/ start byte
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART2_UART_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 71;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 11;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 250000;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_2;
  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();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(TRANSISTOR_GPIO_Port, TRANSISTOR_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : TRANSISTOR_Pin */
  GPIO_InitStruct.Pin = TRANSISTOR_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(TRANSISTOR_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {
    // htim2.Init.Prescaler = 71; // prescale 72 MHz to 1 MHz -> every 1 microsecond
    // htim2.Init.Period = 11; // count from 0 to 11 -> 12 microseconds
    // break for 8 periods (8*12=96) (count to 8), and mark after break for one period (just wait for next loop execution)

    if (DMX_state == 1){ // 0: idle (ready to send), 1: data is available, 2: break on DMX line, 3: mark after break on DMX line, 4: sending data
        HAL_GPIO_WritePin(TRANSISTOR_GPIO_Port, TRANSISTOR_Pin, 1); // pulls low -> break
        DMX_loop_counter = 0;
        DMX_state = 2;
    }
    else if (DMX_state == 2){
        if (DMX_loop_counter < 8) { // wait 8 loop executions
            DMX_loop_counter++;
        }
        else {
            HAL_GPIO_WritePin(TRANSISTOR_GPIO_Port, TRANSISTOR_Pin, 0); // stops pulling low -> mark after break
            DMX_state = 3;
        }
    }
    else if (DMX_state == 3){
        HAL_UART_Transmit_IT(&huart2, DMX_data, sizeof(DMX_data));
        HAL_TIM_Base_Stop_IT(&htim2);
        DMX_state = 4;
    }


}


void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    DMX_state = 0;
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

In the usbd_cdc_if.c, I added something in CDC_Receive_FS():

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  uint32_t length = (uint32_t) *Len;
  uint8_t buffer[length];

  memcpy(buffer, Buf, length);

  extern uint8_t DMX_data[513];
  extern uint8_t DMX_state;
  extern htim2;

  if (length >= 1) {
    if ((buffer[0] == 0x80) && (DMX_state == 0) && (length >= 2)) {
        memset(DMX_data, 0x00, sizeof(DMX_data));
        memcpy(&DMX_data[1], &buffer[1], (length-1));
        HAL_TIM_Base_Start_IT(&htim2);
        DMX_state = 1;
    }
  }
  return (USBD_OK);
  /* USER CODE END 6 */
}

I also enabled the Interrupts for TIM2 and USART2.

\$\endgroup\$
2
  • \$\begingroup\$ Which of all the UARTs? Do you leave 2 stop bits of DMX when going into LIN mode? Show the code to debug this. \$\endgroup\$
    – Justme
    Feb 9 at 23:09
  • \$\begingroup\$ It's USART2. The 2 stop bits are left when switching modes. The signal looks almost the same on the oscilloscope, just scaled along the time axis. I'll add the code ... \$\endgroup\$
    – Felix
    Feb 9 at 23:32

1 Answer 1

1
\$\begingroup\$

The stop bits must be set to 1 in LIN mode.

That and other prerequisites for proper operation in LIN mode is specified in the reference manual for the MCU.

Basically, if you are not using other weird settings, just set stop bits to 1 and it should work.

\$\endgroup\$
6
  • \$\begingroup\$ This solved the issue of the baud rate. However then I don't have my two stop bits which are needed for DMX. So is it just not possible to have two in LIN mode? \$\endgroup\$
    – Felix
    Feb 9 at 23:47
  • \$\begingroup\$ @Felix I don't understand. You either use standard non-LIN asynchronous mode with 2 stop bits for DMX, or, the LIN mode for LIN. They are not compatible. Why would you want to use LIN mode with DMX? \$\endgroup\$
    – Justme
    Feb 9 at 23:50
  • \$\begingroup\$ When trying to receive DMX data, the normal asyncronous UART gets stuck in HAL_ERROR due to the break on the RX line, however from what I've read LIN seems to be able to handle the break \$\endgroup\$
    – Felix
    Feb 9 at 23:54
  • 1
    \$\begingroup\$ @Felix LIN mode is for LIN protocol used in cars. DMX uses standard UART protocol. It will not solve a problem if some HAL cannot handle a line break condition. When you receive a break, handle it properly, with or without the HAL. The HAL is useful to get something working fast, but it is useless if you want to do something special it cannot easily be made to handle, so what I would do is to throw away the HAL and receive the bytes and handle the break in the RX interrupt. \$\endgroup\$
    – Justme
    Feb 10 at 0:01
  • \$\begingroup\$ @Felix or maybe LIN mode could solve the problem. See, if you are receiving data, the second stop bit is ignored anyway. It is the job of transmitter to transmit 2 stop bits, the receiver does not check for 2 stop bits and can be configured to 1 stop bit. \$\endgroup\$
    – Justme
    Feb 10 at 0:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.