cancel
Showing results for 
Search instead for 
Did you mean: 

How to send and receive message through UART interrupt or DMA?

YFuku.3
Associate III

< What I want to do? >

  • Send string messages(AT command) from STM32 microcontroller to LTE module through UART, receive the reply, and store the received data in a string variable. 
  • I tried HAL_UART_Receive function and it works. However, it doesn't complete the job until duration set in the timeout param passes, even if all the bytes are received. I want CPU to move to next job right after receiving all the bytes.

*My Mother tongue is not English. If what I'm writing here is not understandable, let me know. I will fix sentences properly.

 < Environment >

  • Nucleo-L053R8
  • 3G module(HL8548-G)

https://source.sierrawireless.com/resources/airprime/hardware_specs_user_guides/airprime_hl8548_and_hl8548-g_product_technical_specification/

  • The Nucleo-L053R8 and the 3G module should be connected properly.

 < What I tried? >

  • I looked through discussion on some websites and figured out that taking one byte by one from the buffer by HAL_UART_Receive_IT or HAL_UART_Receive_DMA is needed. Then I implemented the below code. However, it does not work well. I'm struggling for this problem for two weeks. I appreciate it if you give me any advice.
  • Settings: 32MHz, using HAL_UART_Receive_DMA(115200baud, Circular mode, no interrupt), and a timer(prescaler:100-1, counter period:320, interrupt, 1msec interruption), 
  • How the below code works
  1.  Initialize UART, timer, and printf function.
  2. (In Func_3G_Comm func)Send string stored in txBuf by HAL_UART_Transmit.
  3. (In Func_3G_Comm func)Enable HAL_UART_Receive_DMA(&huart1, (uint8_t*)&rxBuf_IT, 1);.
  4. (In Func_3G_Comm func)Enter sleep mode.
  5. (In HAL_UART_RxCpltCallback func)When one byte is received, stop a timer, store it in rxBuf, and start the timer again. 
  6. (In HAL_TIM_PeriodElapsedCallback func)When 1msec passes after the last byte is received, this func is called. This means no byte will be received. Set rxFlag = 1.
  7. (In Func_3G_Comm func)Break for loop by rxFlag = 1. Print string in rxBuf
  • The step 7 does not work. Just "AT" characters show up and only blank later.
  • I don't know using DMA is the good way.

extracts from main.c

#include "main.h"
#include "stm32l0xx_hal.h"
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
 
#define txBuf_size 512
#define rxBuf_size 128
 
char txBuf[txBuf_size];
char rxBuf[rxBuf_size];
char superRxBuf[2000];
uint8_t rxFlag = 0;
uint16_t rxIndex = 0;
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);
void Func_3G_Comm(char *Cmd);
 
extern void initialise_monitor_handles(void);
 
int main(void)
{
 initialise_monitor_handles(); // Initialize printf func
 HAL_Init();
 SystemClock_Config();
 
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_USART1_UART_Init();
 MX_TIM2_Init();
 
 /* Perform the initial configuration in 3G module */
 Func_3G_Comm("AT&K3"); // Set hardware flow control
 Func_3G_Comm("AT+KTCPCLOSE=1"); // Close TCP1 connection
 Func_3G_Comm("AT+KTCPDEL=1"); // Close Delete TCP1 connection
 Func_3G_Comm("AT+GPSSTOP"); // Stop GPS
 Func_3G_Comm("AT+GPSNMEA=01,,0"); // Stop receiving GPS NMEA data
 Func_3G_Comm("AT+CGATT=0"); // PS detachment
 Func_3G_Comm("AT+KCNXCFG=1,\"GPRS\",\"***.com\",\"***.com\",\"***\""); // Configure TCP Connection
 Func_3G_Comm("AT+CGDCONT=4,\"IP\",\"***.com\""); // Configure GPRS
 Func_3G_Comm("AT+KCNXPROFILE=1"); // Configure current profile connection
 Func_3G_Comm("AT+KGNSSAD=1"); // Configure GNSS antenna detection
 Func_3G_Comm("AT+CGATT=1"); // PS attachment
 Func_3G_Comm("AT+KTCPCFG=1,0,\"xxxxxx.com\",16650"); // Configure TCP Connection
 Func_3G_Comm("AT+KCNXUP=1"); // Bring up PDP connection
 Func_3G_Comm("AT+KTCPCNX=1"); // Start TCP connection
 
 while (1)
 {
 }
 
}
 
/* USART1 init function */
static void MX_USART1_UART_Init(void)
{
 huart1.Instance = USART1;
 huart1.Init.BaudRate = 115200;
 huart1.Init.WordLength = UART_WORDLENGTH_8B;
 huart1.Init.StopBits = UART_STOPBITS_1;
 huart1.Init.Parity = UART_PARITY_NONE;
 huart1.Init.Mode = UART_MODE_TX_RX;
 huart1.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
 huart1.Init.OverSampling = UART_OVERSAMPLING_16;
 huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
 huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
 
 if (HAL_UART_Init(&huart1) != HAL_OK)
 {
  _Error_Handler(__FILE__, __LINE__);
 }
 
}
 
/* Enable DMA controller clock */
static void MX_DMA_Init(void) 
{
 /* DMA controller clock enable */
 __HAL_RCC_DMA1_CLK_ENABLE();
 
 /* DMA interrupt init */
 /* DMA1_Channel2_3_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 0);
 HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
}
 
void Func_3G_Comm(char *Cmd)
{
  memset(txBuf, '\0', txBuf_size);
  memset(rxBuf, '\0', rxBuf_size);
  snprintf(txBuf, txBuf_size, "%s\r\n", Cmd);
  HAL_UART_Transmit(&huart1, (uint8_t *)txBuf, strlen(txBuf), 0xFFFF);
  HAL_UART_Receive_DMA(&huart1, (uint8_t*)&rxBuf_IT, 1);
  HAL_SuspendTick();
  for(rxFlag=0;rxFlag==0;)
  {
    HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
  }
  HAL_ResumeTick();
  HAL_TIM_Base_Stop_IT(&htim2);
  printf("%s\n",rxBuf);
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  HAL_TIM_Base_Stop_IT(&htim2);
  if(UartHandle->Instance==USART1){
  	rxBuf[rxIndex] = rxBuf_IT;
  	rxIndex++;
  }
  HAL_TIM_Base_Start_IT(&htim2);
  HAL_UART_Receive_DMA(&huart1, (uint8_t*)&rxBuf_IT, 1);
}
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	 if (htim->Instance == TIM2) {
		 rxFlag = 1;
		 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
	 }
}

extracts from stm32l0xx_it.c

void DMA1_Channel2_3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_usart1_rx);
}
 
void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim2);
}

*modification: Func_3G_Comm => LTE_Comm 

17 REPLIES 17
AvaTar
Lead

For a communication based on variable-length packets in both directions, DMA seems a quite unfavourable approach.

An interrupt based system, with an end-character detection in the RX interrupt handler to signal complete packets, would IMHO fit better.

For this kind of application, DMA for transmit may be OK but reception almost certainly requires a different (interrupt-based) approach.

YFuku.3
Associate III

Thank you for your advice!

OK. I'll try to use interrupt func

>> AvaTar

I tried an approach like one you explained; an end-character detection in the RX interrupt handler. However, I didn't come up with a good idea.

one example of rxBuf is like below

AT&K3\r\r\nOK\r\n\r\n+PBREADY\r\n

there are several "\r\n", so it's hard to detect the end of message.

AvaTar
Lead

As I understand it, each "\r\n" terminates an individual message. The reaction depends on the context.

I did not work much with the AT command set, but I think every message is supposed to be terminated with a "\r\n".

That would signal the reception of a complete message, which is then handed to the syntax check.

If that works out well (i.e. proper command), and the semantics are right (expected command in this context, with proper parameters), one could advance the protocol state machine accordingly.

I had used this method with e.g. Modbus and NMEA0183.

My interrupt routine only checked for "\r\n", everything else was done in the main loop (out of interrupt context).

Pavel A.
Evangelist III

Your code does not compile because identifier "3G_Comm" begins with a digit.

Please show the actual code which compiles.

-- pa

YFuku.3
Associate III

>>Pavel

It's a mistake. The original name is "LTE_Comm". I'm developing with 3G module, but will use LTE module in the future so the function is named with "LTE" originally. This is confusing so I modified only the function name. I changed "3G_Comm" to "Func_3G_Comm" and made sure that the function name is complied.

T J
Lead

I cant see where you reset the timecount for timer 2.

YFuku.3
Associate III

>> AvaTar

Thank you for your advice.

In 3G module case, it can add "\r\n" other than in end of a sentence. So detection by catching "\r\n"may be difficult.

It think removing a lot of the HAL nonsense, and just managing the peripheral and buffers in an interrupt, will reduce complexity by an order of magnitude or two.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..