完整的工程间附件,替换\STM32Cube_FW_H5_V1.2.0\Projects\NUCLEO-H533RE\Applications\ThreadX\Tx_Thread_Creation
一.前言
前面我们实现了开发环境的搭建,跑了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解耦应用层和底层,提供给了用户层比较方便的串口读写接口。