0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

UART(四)发送多字节命令

修罗 2025-08-04 269 0条评论 51单片机

首页 / 正文

UART(四)发送多字节命令

1.1 具体要求

  1. 当PC向单片机发送字符串turn on时,单片机需要令LED亮起,并向PC回复:OK: LED turned on\r\n
  2. 当PC向单片机发送字符串off时,单片机需要令LED熄灭,并向PC回复:OK: LED turned off\r\n
  3. 当PC向单片机发送其他字符串时,单片机不做任何操作,只需向PC回复:Error:Unknown command

1.2 实现方式1

特定结束符判断是否接收完毕数据,如回车符:

  1. 定义接收缓冲区和接收到的数据索引以及是否接收完毕的变量:
u8 buffer_str[32]; // 接收缓冲区
u8 buffer_str_len = 0;
bit recv_line_ready;
  1. 中断函数, 接收到\r时将是否接收完毕的变量置1
void Dri_UART_Func() interrupt 4
{
    // 收到命令,将数据放入缓冲区
    if (RI == 1) {
        unsigned char ch = SBUF;
        // 避免数组越界
        if (buffer_str_len < sizeof(buffer_str) - 1) {
            if (ch == '\r') {
                recv_line_ready = 1;
            // 不处理'\n'
            } else if (ch != '\n') {
                buffer_str[buffer_str_len++] = ch;
            }
        }

        RI = 0;
    }
    
    ...
}
  1. 外界循环调用一个函数判断是否接收完毕的变量置1情况,外界提供数组,函数内部将接收到的数组内容循环赋值给外部数组:
bit Dri_UART_RecvStr(char *str)
{
    if(recv_line_ready == 1){
        u8 i = 0;
        while (i < buffer_str_len) {
                str[i] = buffer_str[i];
                i++;
            
        }
        str[i] = '\0';
        buffer_str_len = 0; // 清空缓冲区
        recv_line_ready = 0; // 重置接收状态
        return 1;
    }else {
        return 0; // 没有接收到完整的字符串
    }
    
}
  1. 外界循环调用,收到完整字符串后判断是否为需要的内容,并做相应的处理逻辑:
while (1) {
    if(Dri_UART_RecvStr(buffer_strs) == 1){
        
        if (strcmp(buffer_strs, "turn on") == 0) {
            Dri_UART_SendStr("OK: LED turned on\r\n");
            
        } else if (strcmp(buffer_strs, "off") == 0) {
            Dri_UART_SendStr("OK: LED turned off\r\n");
            
        } else {
            Dri_UART_SendStr("ERROR: Unknown command\r\n");
        }
    }
}

image-20250804104637418.png

完整代码

Dri_UART.h

#ifndef __DRI_UART_H__
#define __DRI_UART_H__

#include <STC89C5xRC.H>
#include "Util.h"

void Dri_UART_Init();
void Dri_UART_Func();
void Dri_UART_SendChar(char str);
void Dri_UART_SendStr(char *str);
bit Dri_UART_RecvStr(char *str);
#endif

Dri_UART.c

#include "Dri_UART.h"

u8 buffer_str[32]; // 接收缓冲区
u8 buffer_str_len = 0;
bit recv_line_ready;

// 发送状态,1:正在发送,0:未在发送
static bit is_sending;
void Dri_UART_Init()
{
    // 1.设置串口工作模式
    SM0 = 0;
    SM1 = 1;
    // 2.设置波特率
    // 2.1 设置SMOD控制位
    PCON &= ~0x80;
    // 2.2设置定时器1工作模式
    TMOD &= 0x0F;
    TMOD |= 0x20;
    // 2.3 设置定时器1初值
    TH1 = 0xFD;
    TL1 = 0xFD;
    // 2.4 启动定时器1
    TR1 = 1;

    // 3.串口接收相关配置
    REN = 1;
    SM2 = 0;

    // 打开中断总开关和串口中断开关
    EA = 1;
    ES = 1;
    TI = 0;
    RI = 0;

    is_sending = 0;
}

void Dri_UART_SendChar(char ch)
{
    // 如果有数据在发送,等待发送完成
    while (is_sending);
    is_sending = 1;
    SBUF = ch;
}

void Dri_UART_SendStr(char *str)
{
    while (*str) {
        Dri_UART_SendChar(*str);
        str++;
    }
}


bit Dri_UART_RecvStr(char *str)
{
    if(recv_line_ready == 1){
        u8 i = 0;
        while (i < buffer_str_len) {
                str[i] = buffer_str[i];
                i++;
            
        }
        str[i] = '\0';
        buffer_str_len = 0; // 清空缓冲区
        recv_line_ready = 0; // 重置接收状态
        return 1;
    }else {
        return 0; // 没有接收到完整的字符串
    }
    
}

/**
 * @brief 串口中断函数,进入这个函数有两个触发条件:发送完成和接收完成
 *
 */
void Dri_UART_Func() interrupt 4
{
    // 收到命令,将数据放入缓冲区
    if (RI == 1) {
        unsigned char ch = SBUF;

        if (buffer_str_len < sizeof(buffer_str) - 1) {
            if (ch == '\r') {
                recv_line_ready = 1;
            } else if (ch != '\n') {
                buffer_str[buffer_str_len++] = ch;
            }
        }

        RI = 0;
    }

     // 发送完成,将发送状态置为0
    if (TI == 1) {
        is_sending = 0;
        TI = 0; // 清除发送中断标志
    }
}

main.c

#include "Dri_Timer0.h"
#include "Dri_UART.h"
#include <STRING.H>
#define BUZZ P46
#define LED    P00
#define LED_OPEN P34

void Int_Buzzer_Buzz()
{
    unsigned char counter = 50;
    // 500Hz的方波
    while (counter) {
        BUZZ = ~BUZZ;
        Delay_1MS(1);
        --counter;
    }
}

void main()
{

    u8 i = 0;
    u8 buffer_strs[32];
    BUZZ = 1; // 打开蜂鸣器
    LED_OPEN = 1;
    
    Dri_UART_Init();
    while (1) {
       if(Dri_UART_RecvStr(buffer_strs) == 1){
            if (strcmp(buffer_strs, "turn on") == 0) {
                // 点亮LED
                P0 = 0x00;
                Dri_UART_SendStr("OK: LED turned on\r\n");
            } else if (strcmp(buffer_strs, "off") == 0) {
                // 熄灭LED
                P0 = 0xFF;
                Dri_UART_SendStr("OK: LED turned off\r\n");
            } else {
                Dri_UART_SendStr("ERROR: Unknown command\r\n");
            }
            // 蜂鸣器响一声
            Int_Buzzer_Buzz();
       }
    }
}

1.3 实现方式2

空闲超过特定时间就表示接收完毕,特点时间内接收数据,每收到一个字符开启新的计时;

Dri_UART.h

#ifndef __DRI_UART_H__
#define __DRI_UART_H__

#include <STC89C5xRC.H>
#include "Util.h"

/**
 * @brief 串口初始化
 *
 */
void Dri_UART_Init(void);

/**
 * @brief 发送字符串
 *
 * @param str 待发送字符串
 */
void Dri_UART_SendStr(char *str);

/**
 * @brief 接收字符串
 *
 * @param str 用于接收字符串的数组
 * @return bit 0为读取失败,1为读取成功
 */
bit Dri_UART_ReceiveStr(char str[]);

#endif /* __DRI_UART_H__ */

Dri_UART.c

#include "Dri_UART.h"
#include "Dri_Timer0.h"

#define BAUD_RATE 9600
#define T2TEMP    256 - (FOSC / NT / 32 / BAUD_RATE)
#include <STDIO.H>

/ 0:未在发送 1:正在发送
static bit s_is_sending = 0;

static char s_buffer[10] = {0};

static u8 s_index = 0;

static bit s_is_complete = 0;

static u8 s_idle_count = 0;

void Dri_UART_Timer0Callback()
{
    s_idle_count++;
    if (s_index > 0 && s_idle_count >= 10) {
        s_is_complete = 1;
        s_idle_count  = 0;
    }
}

void Dri_UART_Init()
{
    // 1.选择工作模式
    SM0 = 0;
    SM1 = 1;

    // 2.波特率
    // 2.1 SMOD
    PCON &= 0x7F;
    // 2.2 Timer1
    // 2.2.1 工作模式
    TMOD &= 0x0F;
    TMOD |= 0x20;
    // 2.2.2 初始值
    TH1 = 0xFD;
    TL1 = 0xFD;
    // 2.2.3 开启
    TR1 = 1;

    // 3.接受相关
    REN = 1;
    SM2 = 0;

    // 4. 中断相关
    EA = 1;
    ES = 1;
    RI = 0;
    TI = 0;

    // 5. 注册空闲监测回调
    Dri_Timer0_RegisterCallback(Dri_UART_Timer0Callback);
}

void Dri_UART_SendChar(char c)
{
    while (s_is_sending == 1);
    s_is_sending = 1;
    SBUF         = c;
}

void Dri_UART_SendStr(char *str)
{
    while (*str != 0) {
        Dri_UART_SendChar(*str);
        str++;
    }
}

bit Dri_UART_ReceiveStr(char *cmd)
{
    u8 i;
    if (s_is_complete) {
        for (i = 0; i < s_index; i++) {
            cmd[i] = s_buffer[i];
        }
        cmd[s_index]  = '\0';
        s_index       = 0;
        s_is_complete = 0;
        return 1;
    } else {
        return 0;
    }
}

void Dri_UART_Handler() interrupt 4
{
    if (RI == 1) {
        // 接收数据的逻辑
        s_idle_count        = 0;
        s_buffer[s_index++] = SBUF;
        RI                  = 0;
    }

    if (TI == 1) {
        // 发送数据的逻辑
        s_is_sending = 0;
        TI           = 0;
    }
}

main.c

#include "Dri_UART.h"
#include <STRING.H>

void main()
{
    char command[10] = {0};
    Dri_UART_Init();
    while (1) {
        if (Dri_UART_ReceiveStr(command)) {
            if (strcmp(command, "on") == 0) {
                P0 = 0x00;
                Dri_UART_SendStr("Ok:LED is on");
            } else if (strcmp(command, "off") == 0) {
                P0 = 0xFF;
                Dri_UART_SendStr("Ok:LED is off");
            } else {
                Dri_UART_SendStr("Error:Unhnown command");
            }
        }
    }
}

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis