FreeRTOS routine 3-serial port interrupt to receive variable length data and use of binary semaphore

1 Basic knowledge points
1.1 Types of
serial port interrupts Serial port interrupts belong to STM32's own resources and do not involve FreeRTOS, but can be used in conjunction with FreeRTOS.

The serial port receive interrupt
interrupt flag is: USART_IT_RXNE, that is, rx none empty, the serial port will trigger an interrupt as long as it receives data. If it is receiving a character string, it will trigger an interrupt every time a character is received.

The serial port idle interrupt
interrupt flag is: USART_IT_IDLE, idle means idle, the interrupt triggered when the serial port is idle, of course, it does not mean that the interrupt is triggered all the time when the serial port is idle, but after each continuous reception is completed, the interrupt is triggered. If a character string is received, an interrupt will be triggered after receiving the entire character string.

Therefore, these two interrupts can be used together. The serial port receives interrupts to receive data in real time. After receiving a string of data, the idle interrupt is triggered, and the received string of data can be analyzed and processed. This way does not need to know the specific length of the string each time, so it can receive serial port data of variable length.

1.2 Semaphore The semaphore in
FreeRTOS is a way of communication between tasks. The semaphore includes: binary semaphore, mutually exclusive semaphore, and counting semaphore. Only binary semaphores are used this time.

Binary
semaphore There are only two states of binary semaphore, which can be understood as a sign, 0 or 1. Semaphores are used for synchronization between tasks. FreeRTOS is a multitasking system. Different tasks may require a certain synchronization relationship. For example, after the serial port interrupts the data received, the data analysis and processing task can get the data for analysis. This is a kind of synchronization.

The basic operations of semaphore include acquiring semaphore and releasing semaphore. For example, when data analysis and processing tasks need to process serial port data, you can first try to acquire the semaphore. If it cannot be obtained, that is, the semaphore is 0, enter the blocking waiting first. , Wait for the timeout to jump out first, and then continue to try to acquire the semaphore. After the serial port idle interrupt receives a string of data, the semaphore release operation can be performed. At this time, the data analysis and processing task can obtain the semaphore, and then can process the serial port data, which realizes the synchronization of the serial port data reception and data processing .

The following program ideas are as follows:

FreeRTOS routine 3-serial port interrupt to receive variable length data and use of binary semaphore

1.3 API function to
create a binary semaphore xSemaphoreCreateBinary()
function prototype (in tasks.c):


SemaphoreHandle_t xSemaphoreCreateBinary( void )

return value:

  • SemaphoreHandle_t: Create a successful binary semaphore handle, return NULL on failure

Release the semaphore xSemaphoreGive()
function prototype (in tasks.c):


BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

parameter:

  • xSemaphore: The semaphore handle to be released

return value:

  • If the release is successful, it returns pdPASS, if it fails, it returns errQUEUE_FULL

Release the semaphore (in the interrupt function) xSemaphoreGiveFromISR()


BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
                                 BaseType_t* pxHigherPriorityTaskWoken)

parameter:

  • xSemaphore: Same as above

  • pxHigherPriorityTaskWoken: mark whether task switching is required after exiting this function

return value:

  • Same as above

Get the semaphore xSemaphoreTake()
function prototype (in tasks.c):


BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
                          TickType_t xBlockTime)

parameter:

  • xSemaphore: The semaphore handle to be released

  • xBlockTime: Blocking time

return value:

  • Return pdTRUE for success, pdFALSE for failure

Get semaphore (in interrupt function) xSemaphoreTakeFromISR()


BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
                                 BaseType_t* pxHigherPriorityTaskWoken)

parameter:

  • xSemaphore: Same as above

  • pxHigherPriorityTaskWoken: mark whether task switching is required after exiting this function

return value:

  • Same as above

2 Programming Points
2.1 Serial Port Interruption and Release Semaphore
When configuring serial port, remember to enable two interrupts.

//=======================================
//初始化IO 串口1
//bound:波特率
//=======================================
void uart_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟

    //串口1对应引脚复用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1

    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

    //USART1 初始化设置
    USART_InitStructure.USART_BaudRate = bound;//波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口1

    USART_Cmd(USART1, ENABLE);  //使能串口1

    USART_ClearFlag(USART1, USART_FLAG_TC);

#if EN_USART1_RX
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=8;//抢占优先级8
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

#endif

}

For the serial port idle interrupt of the interrupt service function, the flag bit can only be cleared by reading the SR register first, and then reading the DR register!

Use the semaphore in the interrupt to release the function xSemaphoreGiveFromISR at the end of the ISR, otherwise the program will get stuck.


//=======================================
//串口1中断服务程序
//=======================================
void USART1_IRQHandler(void)                    
{
    uint8_t data;//接收数据暂存变量
    BaseType_t xHigherPriorityTaskWoken;

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
    {
        data =USART_ReceiveData(USART1);            
        Recv[rx_cnt++]=data;//接收的数据存入接收数组

        USART_ClearITPendingBit(USART1,USART_IT_RXNE);
    }

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空闲中断
    {
        if(uartSemaphore!=NULL)
        {
            //释放二值信号量
            xSemaphoreGiveFromISR(uartSemaphore,&xHigherPriorityTaskWoken); //释放二值信号量
        }
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换

        data = USART1->SR;//串口空闲中断的中断标志只能通过先读SR寄存器,再读DR寄存器清除!
        data = USART1->DR;
        //USART_ClearITPendingBit(USART1,USART_IT_IDLE);//这种方式无效

        //rx_cnt=0;
     }
}

2.2 Obtaining the semaphore
Write a task to obtain the serial port data. The task keeps trying to obtain the semaphore. After the acquisition is successful, the data is processed.

Obtain the semaphore xSemaphoreTake, block (waiting time) 10ms, and execute downward if the semaphore is not obtained. Each task is an infinite loop, and the semaphore acquisition will be performed immediately.


//打印任务函数
void print_task(void *pvParameters)
{
    int count=0;
    BaseType_t err = pdFALSE;

    int size=50;
    uint8_t buf[64];//最多只取前64个数据

    //清空本地接收数组
    memset(buf,0,size);

    while(1)
    {
        err=xSemaphoreTake(uartSemaphore,10);   //获取信号量
        if(err==pdTRUE)                         //获取信号量成功
        {  
            //printf("%s",Data);
            if(rx_cnt < size)//收到的数据长度在size范围内
            {
                //void *memcpy(void *str1, const void *str2, size_t n)  
                //从存储区 str2 复制 n 个字节到存储区 str1。
                memcpy(buf,Recv,rx_cnt);//有几个复制几个
                count=rx_cnt;
                //printf("%s\r\n", buf);
            }
            else//收到的数据长度太长了
            {
                memcpy(buf,Recv,size);//只复制size个
                count=size;
            }
            rx_cnt=0;
        }

        if(count>0)
        {
            count=0;
            printf("receive:%s",buf);

            //------------------------------------------------------------------------------
            //这里可以继续对buf进行分析和处理,比如根据buf的不同内容执行不同的小任务

        }
    }
}

2.3 A small application
combined with the relevant knowledge of string operations introduced in the previous article: C language string related functions use the example strtok_r strstr strtok atoi, which can process "command + parameter" string data.

//先判断指令名称
char *cmd;//表示命令
char *paras;//表示命令后的参数
cmd = strtok_r((char*)buf, " ", &paras);//这里有点小问题,不带参数的命令,后面需要一个空格

char *ret;
int i;
for (i = 0; i < N;i++)
{
    ret = strstr(struct_dostr1[i].name, cmd);
    if(ret!=NULL)
    {
//        printf("find cmd in funname[%d]\r\n", i);
        break;
    }
}
if(i==N)
{
    printf("can't find cmd in funname[]\r\n");
}
else
{               
    //是有效的指令,继续判断后续参数
    char* para[4]={0};//限定最多接收4个参数
    para[0] = strtok(paras, " ");
    int j= 1;
    while(paras != NULL)//这里有点小问题,不可以提前结束
    {
        para[j++] = strtok(NULL, " ");
        if(j==4)
            break;
    }

    //执行对应的函数
    struct_dostr1[i].fun(para);
}

The final function execution is to associate the character command with the function pointer by defining a structure:

#define N 2
typedef struct struct_dostr
{
char name[32];
int (*fun)(char *argv[]);
}struct_dostr;

struct_dostr struct_dostr1[N]={
{"hello",hello},
{"led",  led},  
};

int hello(char* p[])
{
    printf("hello~~~~~~~~~~\r\n");
    return 0;
}

int led(char* p[])
{
    int p0,p1;
    p0=atoi(p[0]);
    p1=atoi(p[1]);

    printf("get led: %d, %d\r\n",p0,p1);
    return 0;
}

3 The experimental results
send hello or led 80 5 through the serial port, and you can see the desired processing results:


receive:hello  
hello~~~~~~~~~~
receive:led 80 5
get led: 80, 5

The complete project code has been saved to GitHub:

https://github.com/xxpcb/FreeRTOS-STM32F407-examples

Guess you like

Origin blog.51cto.com/15060517/2641135