782|4

501

帖子

4

TA的资源

纯净的硅(高级)

楼主
 

【NUCLEO H533RE】使用FIFO实现方便好用的串口的读写接口 [复制链接]

完整的工程间附件,替换\STM32Cube_FW_H5_V1.2.0\Projects\NUCLEO-H533RE\Applications\ThreadX\Tx_Thread_Creation

Tx_Thread_Creation.zip (156.76 KB, 下载次数: 0)

 

 

 

一.前言

前面我们实现了开发环境的搭建,跑了Demo进行串口收发测试,但是接口不方便应用层使用,

所以我们在此基础上基于FIFO修改串口的收发接口,给应层提供好用的接口。

这此在Demo STM32Cube_FW_H5_V1.2.0\Projects\NUCLEO-H533RE\Applications\ThreadX\Tx_Thread_Creation\MDK-ARM\Tx_Thread_Creation.uvprojx

上的基础上修改。

 

二.FIFO实现

FIFO的实现参考,公众号”嵌入式Lee”的文章,https://mp.weixin.qq.com/s/PV-sUxzTEKbobgyt4BKRlA 超级精简系列之十九:超级精简的循环FIFO池,C实现.这里不再赘述,直接将fifo.c和fifo.h拿过来使用,这两个文件无需任何修改。

fifo.c

#include <string.h>
#include "fifo.h"

#define FIFO_PARAM_CHECK 0

/**
 * in为写入索引 0~(buffer_len-1)。
 * out为读出索引 0~(buffer_len-1)。
 * in == out时可能是满,也可能是空,可以通过len有效数据长度来确认。
 * 写数据in增加,直到追赶到out则满。
 * 读数据则out增加,直到追赶到in则空。
 * in大于out时则[out,in)区间是有效数据。
 * in小于out时则[out,buffer_len)和[0,in)区间是有效数据。
 ***********************************************************
 *     0                                 buffer_len-1 buffer_len
 *     (1)开始 in和out都是0
 *     |                                             |
 *     in(0)
 *     out(0)
 *     len = 0
 *     (2)写入n字节数据 in变为n和out还是0 对应in大于out的情况
 *     |                                             |
 *     out(0)————————————>in(n)                      |   
 *     len = n
 *     (3)读出m字节数据(m<n) in还是n和out变为m 对应in大于out的情况
 *     |                                             |
 *             out(m)————>in(n)
 *     len = n-m
 *     (4)继续写入数据,绕回到开头,对应in小于out的情况
 *     |                                             |
 *             out(m)————————————————————————————————>
 *     ——>in(k)
 *     len = k + buffer_len-m
 */
uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len)
{
  uint32_t space = 0;  /* 用于记录空闲空间大小 */
  /* 参数检查 */
  #if FIFO_PARAM_CHECK
  if((dev == 0) || (buffer == 0) || (len == 0))
  {
    return 0;
  }
  if(dev->buffer == 0)
  {
    return 0;
  }
  #endif

  /* 限制len的最大长度为buffer大小 */
  if(len > dev->buffer_len)
  {
    len = dev->buffer_len;
  }

  /* 计算空闲空间大小 
   * 正常dev->len不应该大于dev->buffer_len
   */
  if(dev->buffer_len >= dev->len)
  {
    space = dev->buffer_len - dev->len; 
  }
  else
  {
    /* 这里不应该出现, 出现则是异常 */
    dev->len = 0;
    space = dev->buffer_len; 
  }

  /* 计算待写入大小, 如果len大于剩余空间则只写入剩余空间大小 */
  len = (len >= space) ? space : len;  
  if(len == 0)
  {
    return 0; /* 这里有可能无剩余空间,直接返回 */
  }

  /* 计算len的长度是否需要有绕回,需要分次写入 */
  space = dev->buffer_len - dev->in; /* 当前写入位置in到缓存末尾剩余可写入空间 */
  if(space >= len)
  {
    /* 当前写入位置in到缓存末尾足够一次写入 */
    memcpy(dev->buffer+dev->in,buffer,len);
  }
  else
  {
    /* 当前写入位置in到缓存末尾不够,还需要绕回到前面写 */
    memcpy(dev->buffer+dev->in,buffer,space);    /* 先写入tail部分  */
    memcpy(dev->buffer,buffer+space,len-space);  /* 再写入绕回头部分 */
  } 
  /* 更新写入索引和有效数据长度 */
  dev->in += len;
  if(dev->in >= dev->buffer_len)
  {
    dev->in -= dev->buffer_len;  /* 判断加减法 替代 dev->in %= dev->buffer->len */
  }
  dev->len += len;  /* dev->len最大dev->buffer->len,无需%= dev->buffer->len */
  return len;
}

uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len)
{
  uint32_t space = 0; 
  /* 参数检查 */
  #if FIFO_PARAM_CHECK
  if((dev == 0) || (buffer == 0) || (len == 0))
  {
    return 0;
  }
  if(dev->buffer == 0)
  {
    return 0;
  }
  #endif
  
  /* 判断是否有数据 */
  if(dev->len == 0)
  {
    return 0;
  }

  /* 可读出数据量取需要的和有的之间的小值 */
  len = (dev->len) > len ? len : dev->len;

  /* 计算len的长度是否需要有绕回,需要分次读出 */
  space = dev->buffer_len - dev->out; /* 当前读出位置out到缓存末尾剩余可读出空间 */
  if(space >= len)
  {
    /* 当前读出位置out到缓存末尾足够一次读出 */
    memcpy(buffer,dev->buffer+dev->out,len);
  }
  else
  {
    /* 当前读出位置out到缓存末尾不够,还需要绕回到前面读 */
    memcpy(buffer,dev->buffer+dev->out,space);    /* 先读出tail部分  */
    memcpy(buffer+space,dev->buffer,len-space);   /* 再读出绕回头部分 */
  } 
  /* 更新读出索引和有效数据长度 */
  dev->out += len;
  if(dev->out >= dev->buffer_len)
  {
    dev->out -= dev->buffer_len;  /* 判断加减法 替代 dev->out %= dev->buffer->len */
  }
  dev->len -= len;   /* 这里dev->len 不可能小于len,不会溢出 */
  return len;
}

uint32_t fifo_getlen(fifo_st* dev)
{
  #if FIFO_PARAM_CHECK
  if(dev == 0)
  {
    return 0;
  }
  #endif
  return dev->len;
}

void fifo_clean(fifo_st* dev)
{
  #if FIFO_PARAM_CHECK
  if(dev == 0)
  {
    return 0;
  }
  #endif
  dev->len = 0;
  dev->in = 0;
  dev->out = 0;
}

fifo.h

#ifndef FIFO_H
#define FIFO_H

#ifdef __cplusplus
extern "C" {
#endif
  
#include <stdint.h>

/**
 * \struct fifo_st
 * FIFO缓冲区结构.
 */
typedef struct 
{
  uint32_t in;          /**< 写入索引        */ 
  uint32_t out;         /**< 读出索引        */ 
  uint32_t len;         /**< 有效数据长度    */ 
  uint32_t buffer_len;  /**< 有效长度        */ 
  uint8_t* buffer;      /**< 缓存,用户分配   */

} fifo_st;
  
/**
 * \fn fifo_in
 * 往fifo里写数据
 * \param[in] dev \ref fifo_st
 * \param[in] buffer 待写入的数据
 * \param[in] len 待写入的长度
 * \retval 返回实际写入的数据量
 */
uint32_t fifo_in(fifo_st* dev, uint8_t* buffer, uint32_t len);

/**
 * \fn fifo_out
 * 从fifo读出数据
 * \param[in] dev \ref fifo_st
 * \param[in] buffer 存读出的数据
 * \param[in] len 需要读出的数据长度
 * \retval 返回实际读出的数据量
 */
uint32_t fifo_out(fifo_st* dev, uint8_t* buffer, uint32_t len);

uint32_t fifo_getlen(fifo_st* dev);

void fifo_clean(fifo_st* dev);
#ifdef __cplusplus
}
#endif

#endif

 

  • 接口实现

将fifo.c和fifo.h添加到工程,并添加uart_task.c和uart_task.h两个文件。

 

Uart_task.c和uart_task.h最终内容如下,

uart_task.c

#include <stdio.h>
#include "tx_api.h"
#include "stm32h5xx_hal.h"
#include "stm32h5xx_ll_rcc.h"
#include "stm32h5xx_ll_usart.h"
#include "fifo.h"
#include "uart_task.h"

static TX_THREAD uart_thread;
static UART_HandleTypeDef huart1;
static uint8_t tx_fifo_buffer[2048];
static uint8_t rx_fifo_buffer[2048];

static fifo_st tx_fifo_dev=
{
  .in=0,
  .out=0,
  .len=0,
  .buffer_len=sizeof(tx_fifo_buffer),
  .buffer=tx_fifo_buffer
};


static fifo_st rx_fifo_dev=
{
  .in=0,
  .out=0,
  .len=0,
  .buffer_len=sizeof(rx_fifo_buffer),
  .buffer=rx_fifo_buffer
};

static void uart_init(void)
{
	//LL_USART_ClockInitTypeDef USART_ClockInitStruct;
	//LL_USART_ClockStructInit(&USART_ClockInitStruct);
	//LL_USART_ClockInit(USART2, &USART_ClockInitStruct);
	
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART2;
    PeriphClkInitStruct.Usart1ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
			while(1);
    }

    /* Peripheral clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    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);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);

		LL_USART_InitTypeDef USART_InitStruct;
		LL_USART_StructInit(&USART_InitStruct);
		USART_InitStruct.BaudRate = 115200;
		USART_InitStruct.DataWidth= LL_USART_DATAWIDTH_8B,
		USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
		USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
		USART_InitStruct.Parity = LL_USART_PARITY_NONE;
		USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
		USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
		USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
		LL_USART_Init(USART2, &USART_InitStruct);
		LL_USART_EnableIT_RXNE(USART2);
		LL_USART_Enable(USART2);
		
#if 0
  huart1.Instance = USART2;
  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_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
		while (1)
		{
		}
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
		while (1)
		{
		}
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
		while (1)
		{
		}
  }
  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
  {
		while (1)
		{
		}
  }
#endif
}
static void uart_task_entry(ULONG thread_input)
{
	uart_init();
	printf("start uart task\r\n");
	while(1)
	{
		  uint32_t len;
	    /* tx */
		  do
      {
				__disable_interrupts();
				len = fifo_out(&tx_fifo_dev,tx_fifo_buffer,sizeof(tx_fifo_buffer));
				__enable_interrupts();	
				if(len > 0)
				{
					for(uint32_t i=0 ;i<len; i++)
					{
						LL_USART_TransmitData8(USART2,tx_fifo_buffer[i]);
						while(LL_USART_IsActiveFlag_TC(USART2) == 0);
					}
				}
			}while(len >0 );
			tx_thread_sleep(1);
	}
	
}


UINT uart_task_init(CHAR * memory_ptr)
{
		UINT ret = TX_SUCCESS;
		TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;

		/* USER CODE BEGIN App_ThreadX_MEM_POOL */

		/* USER CODE END App_ThreadX_MEM_POOL */
		CHAR *pointer;

		/* Allocate the stack for Main Thread  */
		if (tx_byte_allocate(byte_pool, (VOID**) &pointer,
												 512, TX_NO_WAIT) != TX_SUCCESS)
		{
			return 0;
		}
		
		if (tx_thread_create(&uart_thread, "uart thread", uart_task_entry, 0, pointer,
												 512, 5, 5,
												 TX_NO_TIME_SLICE, 1) != TX_SUCCESS)
		{
			return TX_THREAD_ERROR;
		}
		else
    {
		    return TX_SUCCESS;
		}
}


uint32_t uart_write(uint8_t* buffer, uint32_t len)
{
	uint32_t slen = 0;
	__disable_interrupts();
	slen = fifo_in(&tx_fifo_dev, buffer, len);
	__enable_interrupts();
	return slen;
}

uint32_t uart_read(uint8_t* buffer, uint32_t len)
{
	uint32_t rlen = 0;
	__disable_interrupts();
	rlen = fifo_out(&rx_fifo_dev, buffer, len);
	__enable_interrupts();
	return rlen;
}

void uart_get_data_cb(uint8_t* buffer,uint32_t len)
{
	__disable_interrupts();
	fifo_in(&rx_fifo_dev, buffer, len);
	__enable_interrupts();
}

uart_task.h

#ifndef UART_TASK_H
#define UART_TASK_H

#ifdef __cplusplus
extern "C" {
#endif
  
#include <stdint.h>

UINT uart_task_init(CHAR * memory_ptr);
uint32_t uart_write(uint8_t* buffer, uint32_t len);
uint32_t uart_read(uint8_t* buffer, uint32_t len);
void uart_get_data_cb(uint8_t* buffer,uint32_t len);

#ifdef __cplusplus
}
#endif

#endif

 

四.测试

添加main_task.c和main_task.h内容如下

main_task.c

#include <stdio.h>
#include "tx_api.h"
#include "stm32h5xx_hal.h"
#include "uart_task.h"

static TX_THREAD main_thread;

uint8_t tx_buffer[]={"Hello World\r\n"};
uint8_t rx_buffer[]={"Hello World\r\n"};

static void main_task_entry(ULONG thread_input)
{
	tx_thread_sleep(10);
	while(1)
	{
			uint32_t len = uart_read(rx_buffer,sizeof(rx_buffer));
		  if(len > 0)
      { 
					uart_write(rx_buffer,len);
			}
			//uart_write(tx_buffer,sizeof(tx_buffer));
			tx_thread_sleep(1);
	}
}


UINT main_task_init(CHAR * memory_ptr)
{
		UINT ret = TX_SUCCESS;
		TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;

		/* USER CODE BEGIN App_ThreadX_MEM_POOL */

		/* USER CODE END App_ThreadX_MEM_POOL */
		CHAR *pointer;

		/* Allocate the stack for Main Thread  */
		if (tx_byte_allocate(byte_pool, (VOID**) &pointer,
												 512, TX_NO_WAIT) != TX_SUCCESS)
		{
			return 0;
		}
		
		if (tx_thread_create(&main_thread, "main thread", main_task_entry, 0, pointer,
												 512, 5, 5,
												 TX_NO_TIME_SLICE, 1) != TX_SUCCESS)
		{
			return TX_THREAD_ERROR;
		}
		else
    {
		    return TX_SUCCESS;
		}
}

main_task.h

#ifndef MAIN_TASK_H
#define MAIN_TASK_H

#ifdef __cplusplus
extern "C" {
#endif
  
#include <stdint.h>

UINT main_task_init(CHAR * memory_ptr);

#ifdef __cplusplus
}
#endif

#endif

app_azure_rtos.c中创建任务

UINT App_ThreadX_Init(VOID *memory_ptr)
{
#if 0
  UINT ret = TX_SUCCESS;
  TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;

  /* USER CODE BEGIN App_ThreadX_MEM_POOL */

  /* USER CODE END App_ThreadX_MEM_POOL */
  CHAR *pointer;

  /* Allocate the stack for Main Thread  */
  if (tx_byte_allocate(byte_pool, (VOID**) &pointer,
                       TX_APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
  {
    return TX_POOL_ERROR;
  }
  /* Create Main Thread.  */
  if (tx_thread_create(&tx_app_thread, "Main Thread", MainThread_Entry, 0, pointer,
                       TX_APP_STACK_SIZE, TX_APP_THREAD_PRIO, TX_APP_THREAD_PREEMPTION_THRESHOLD,
                       TX_APP_THREAD_TIME_SLICE, TX_APP_THREAD_AUTO_START) != TX_SUCCESS)
  {
    return TX_THREAD_ERROR;
  }

  /* USER CODE BEGIN App_ThreadX_Init */
  /* Allocate the stack for ThreadOne.  */
  if (tx_byte_allocate(byte_pool, (VOID **) &pointer,
                       TX_APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
  {
    return TX_POOL_ERROR;
  }

  /* Create ThreadOne.  */
  if (tx_thread_create(&ThreadOne, "Thread One", ThreadOne_Entry, 0, pointer,
                       TX_APP_STACK_SIZE, THREAD_ONE_PRIO, THREAD_ONE_PREEMPTION_THRESHOLD,
                       TX_APP_THREAD_TIME_SLICE, TX_APP_THREAD_AUTO_START) != TX_SUCCESS)
  {
    return TX_THREAD_ERROR;
  }

  /* Allocate the stack for ThreadTwo.  */
  if (tx_byte_allocate(byte_pool, (VOID **) &pointer,
                       TX_APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
  {
    return TX_POOL_ERROR;
  }

  /* Create ThreadTwo.  */
  if (tx_thread_create(&ThreadTwo, "Thread Two", ThreadTwo_Entry, 0, pointer,
                       TX_APP_STACK_SIZE, THREAD_TWO_PRIO, THREAD_TWO_PREEMPTION_THRESHOLD,
                       TX_APP_THREAD_TIME_SLICE, TX_APP_THREAD_AUTO_START) != TX_SUCCESS)
  {
    return TX_THREAD_ERROR;
  }

  /* Create the event flags group.  */
  if (tx_event_flags_create(&EventFlag, "Event Flag") != TX_SUCCESS)
  {
    return TX_GROUP_ERROR;
  }
  /* USER CODE END App_ThreadX_Init */

  return ret;
#else
    UINT ret = TX_SUCCESS;
    TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
		ret = uart_task_init(memory_ptr);
    if (ret != TX_SUCCESS)
    {
      /* USER CODE BEGIN  App_ThreadX_Init_Error */
      Error_Handler();
      /* USER CODE END  App_ThreadX_Init_Error */
    }
		ret = main_task_init(memory_ptr);
    if (ret != TX_SUCCESS)
    {
      /* USER CODE BEGIN  App_ThreadX_Init_Error */
      Error_Handler();
      /* USER CODE END  App_ThreadX_Init_Error */
    }	
		return 0;
#endif
}

读到数据原样返回

 

 

五.总结

以上通过FIFO解耦应用层和底层,提供给了用户层比较方便的串口读写接口。

此帖出自stm32/stm8论坛

最新回复

高主频MCU下,对于串口的发送与接收使用FIFO策略还是非常实用的。   详情 回复 发表于 2024-7-18 10:25
点赞 关注
 

回复
举报

6841

帖子

11

TA的资源

版主

沙发
 

占了4K的缓冲区,太大方了吧。

此帖出自stm32/stm8论坛

点评

可以按需调整,这个芯片ram大,可以豪横点,一般情况都只舍得128,256字节,小气惯了用到资源丰富的芯片败家一把。  详情 回复 发表于 2024-7-8 11:56
 
 

回复

501

帖子

4

TA的资源

纯净的硅(高级)

板凳
 
lugl4313820 发表于 2024-7-8 07:05 占了4K的缓冲区,太大方了吧。

可以按需调整,这个芯片ram大,可以豪横点,一般情况都只舍得128,256字节,小气惯了用到资源丰富的芯片败家一把。

此帖出自stm32/stm8论坛
 
 

回复

15

帖子

0

TA的资源

一粒金砂(中级)

4
 

楼主的分享十分有用,先收藏起来,慢慢学习,非常感谢楼主的无私奉献

此帖出自stm32/stm8论坛
 
 
 

回复

623

帖子

0

TA的资源

纯净的硅(高级)

5
 

高主频MCU下,对于串口的发送与接收使用FIFO策略还是非常实用的。

此帖出自stm32/stm8论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表