找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5189|回复: 28
收起左侧

51单片机串口怎么接收数字(不是字符)?

  [复制链接]
回帖奖励 25 黑币 回复本帖可获得 5 黑币奖励! 每人限 1 次
ID:570093 发表于 2021-5-27 23:40 | 显示全部楼层 |阅读模式
我使用一个上位机通过串口发送数据给单片机,但是只能发送单个字符才能控制单片机。怎么发送字符串让单片机接收后,把这个字符串转为数字?网上找参考程序搞了一个晚上也弄不出来。void Uart_Isr() interrupt 4
{
                                                                    
        uchar receive_data;

        EA = 0;
          if(RI == 1)
        {
                    RI = 0;
                    receive_data = SBUF;
                if(receive_data=='0')
                {
                        LED=0;        
                }
                else if(receive_data=='1')
                {
                        LED=1;               
                }
                else
                {

                               //处理我发送的字符串数据
                   }         
        TI=0;   
        EA = 1;                                                                     
}

回复

使用道具 举报

ID:213173 发表于 2021-5-28 06:15 | 显示全部楼层
串口一次只能传输1个字节,传输字符串就是连续传输若干字节。传输的是数字或字符与串口功能本身无关,是由通信协议确定的。一个最简单的自定义通信协议通常由若干字节组成一帧数据。串口中断函数只负责把收到的一帧数据按序保存在缓冲数组中,只发送接收完成标志,不对数据进行处理。在主函数中判断接收完成后对收到的数据进行解析处理,执行相关任务。

  1. void UARTInterrupt() interrupt 4
  2. {
  3.         static unsigned char num=0;        //静态变量
  4.         RI=0;                                        //接收中断请求标志位清0
  5.         rec_buf[num]=SBUF;                //保存SBUF接收到的数据
  6.         if(rec_buf[0]==0xaa)        //验证数据头,否则被下一个数据覆盖
  7.         {
  8.                 num++;
  9.                 if(num>=x)                        //x=字符串长度
  10.                 {
  11.                         flag=1;                        //接收完成标志置1
  12.                         num=0;
  13.                 }
  14.         }
  15. }
复制代码
回复

使用道具 举报

ID:744327 发表于 2021-5-28 08:29 | 显示全部楼层
串行通信一次只能传输1个字节,若传输多个字节或传输字符串,需要连续传输若干字节。
回复

使用道具 举报

ID:97350 发表于 2021-5-28 08:57 | 显示全部楼层
#include<reg52.h>

//------------------串口通信的数据包协议-----------------//
/*
    此程序的串口字符串通信使用到下面的一个自定义协议,每次通信都是发送或接收一个数据包,数据包格式解释如下(长度恒为15):
    例如:A01_fmq_01Off___#
    A--------数据包的开始标记(可以为A到Z,意味着数据包可以有26种)
    01-----设备代号
    fmq_01Off___--------指令(长度恒为10),指令的前4个人字符是指令头部,指令的后6个字符是指令尾部
    #---------数据包的结束标记

    例如:A02_SenT010250#
    A--------数据包的开始标记(可以为A到Z,意味着数据包可以有26种)
    02-----设备代号
    SenT010250--------指令(长度恒为10),指令的前4个人字符是指令头部,指令的后6个字符是指令尾部
    #---------数据包的结束标记
*/
char RecvString_buf[16];            //定义数据包长度为15个字符
#define deviceID_1Bit '0'                //用于串口通信时,定义本地设备ID的第1位
#define deviceID_2Bit '2'                //用于串口通信时,定义本地设备ID的第2位
#define datapackage_headflag 'A'        //用于串口通信时,定义数据包头部的验证标记

char DataPackage_DS18B20[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,'_','S','e','n','T','X','X','X','X','X','X','#'};    //这个是曾经用来控制温度传感模块(DS18B20)的数据包
char HeartBeat[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,'_','B','e','a','t','X','X','X','X','X','X','#'};            //我随便定义了一个数据包用来做"心跳包",比如单片机系统向电脑每2秒发送一次该数据包,如果电脑没有按时接收到,就认为单片机死掉了
//----------------------------------------------//
/*******************************
            串口通信
    MCU:89C52RC        11.0592MHz

//11.0592MHz 0xd0 1200bps
//12MHz 0xcc 1200bps
//11.0592MHz 0xfa 9600bps
//0xf4 11.0592MHz  0xf3 12MHz 4800bps
//均在SMOD=1的情况下(波特率倍增模式)
*******************************/
//串口发送函数
void PutString(unsigned char *TXStr)
{
    ES=0;
     while(*TXStr!=0)
    {
        SBUF=*TXStr;
        while(TI==0);
        TI=0;
        TXStr++;
    }
    ES=1;
}
//串口接收函数
bit ReceiveString()
{
    char * RecStr=RecvString_buf;
    char num=0;
    unsigned char count=0;
    loop:
    *RecStr=SBUF;
    count=0;
    RI=0;
    if(num<14)  //数据包长度为15个字符,尝试连续接收15个字符
    {
        num++;
        RecStr++;
        while(!RI)
        {
            count++;
            if(count>130)return 0;    //接收数据等待延迟,等待时间太久会导致CPU运算闲置,太短会出现"数据包被分割",默认count=130
        }
        goto loop;
    }
    return 1;
}
//定时器1用作波特率发生器
void Init_USART()
{
    SCON=0x50;  //串口方式1,使能接收
    TMOD|=0x20;  //定时器1工作方式2(8位自动重装初值)
    TMOD&=~0x10;
    TH1=0xfa;   //9600bps
    TL1=0xfa;
    PCON|=0x80;  //SMOD=1
    TR1=1;
    TI=0;
    RI=0;
    //PS=1;   //提高串口中断优先级
    ES=1;  //开启串口中断使能
}
//比较指令头部
bit CompareCMD_head(char CMD_head[])
{
    unsigned char CharNum;
    for(CharNum=0;CharNum<4;CharNum++)  //指令长度为10个字符
    {
        if(!(RecvString_buf[CharNum+4]==CMD_head[CharNum]))
        {
            return 0;  //指令头部匹配失败
        }
    }
    return 1;        //指令头部匹配成功
}
//比较指令尾部(start:从哪里开始比较,quality:比较多少个字符,CMD_tail[]:要比较的字符串)
bit CompareCMD_tail(unsigned char start,unsigned char quality,char CMD_tail[])
{
    unsigned char CharNum;
    for(CharNum=0;CharNum<quality;CharNum++)
    {
        if(!(RecvString_buf[start+CharNum]==CMD_tail[CharNum]))
        {
            return 0;
        }
    }
    return 1;
}
bit Deal_UART_RecData()   //处理串口接收数据包函数(成功处理数据包则返回1,否则返回0)
{
    //PutString(RecvString_buf);
    if(RecvString_buf[0]==datapackage_headflag&&buf_string[14]=='#')  //进行数据包头尾标记验证
    {
        switch(RecvString_buf[1])        //识别发送者设备ID的第1位数字
        {
            case '0':
                switch(RecvString_buf[2])        //识别发送者设备ID的第2位数字
                {
                    case '3':
                        if(CompareCMD_head("Ligt"))    //判断指令头部是否为"Ligt"
                        {
                            //下面是指令尾部分析
                            switch(RecvString_buf[8])
                            {
                                case '0':
                                    switch(RecvString_buf[9])
                                    {
                                        case '0':

                                            return 0;
                                        case '1':
                                            if(CompareCMD_tail(10,3,"Off"))   //判断整个数据包是否为:A03_Ligt01Off_#
                                            {
                                                //如果是则执行以下代码
                                                return 1;
                                            }
                                            if(CompareCMD_tail(10,3,"On_"))    //判断整个数据包是否为:A03_Ligt01On__#
                                            {
                                                //如果是则执行以下代码
                                                return 1;
                                            }
                                            return 0;
                                        default:
                                            return 0;
                                    }
                                default:
                                    return 0;
                            }
                        }
                        return 0;

                    default:
                        return 0;
                }
            default:
                return 0;
        }
    }
    return 0;
}
/************************
        中断函数
************************/
//串口中断服务函数-----------
void USART() interrupt 4   //标志位TI和RI需要手动复位,TI和RI置位共用一个中断入口
{
    if(ReceiveString())
    {
        //数据包长度正确则执行以下代码
        Deal_UART_RecData();
    }
    else
    {
        //数据包长度错误则执行以下代码
        //LED1=~LED1;
    }
    RI=0;  //接收并处理一次数据后把接收中断标志清除一下,拒绝响应在中断接收忙的时候发来的请求
}
/***************************
        主函数
***************************/
void main()
{
    EA=1;
    Init_USART();    //初始化串口中断通信,当串口接受完数据包后,如果检测到数据包包含有效指令,则自动执行对应的代码,执行完自动返回到主函数,为了尽可能不影响主函数的时序,串口中断函数的执行代码不要过复杂
    while(1)
    {
    //下面可以放要经常运行的用户代码,使用PutString()函数来发送数据包,如PutString(HeartBeat);    注:空格的ASCLL码是:0x20,回车是:0x0D


    }
}
回复

使用道具 举报

ID:277550 发表于 2021-5-28 09:02 | 显示全部楼层
先理解数据类型,和存储方法

像int是2个byte、所以串口通讯要传输2个字节,接收端收到后、再组合起来
回复

使用道具 举报

ID:570093 发表于 2021-5-28 09:12 | 显示全部楼层
wulin 发表于 2021-5-28 06:15
串口一次只能传输1个字节,传输字符串就是连续传输若干字节。传输的是数字或字符与串口功能本身无关,是由 ...

那就是串口接收字符串后怎么转为数字?比如发送“123”,接收到字符串怎么转为整型数字123?
回复

使用道具 举报

ID:213173 发表于 2021-5-28 15:27 | 显示全部楼层
96317 发表于 2021-5-28 09:12
那就是串口接收字符串后怎么转为数字?比如发送“123”,接收到字符串怎么转为整型数字123?

0~9数字转字符ASCII码+48(0x30),同理0~9字符转数字-48(0x30)。
无标题.jpg

回复

使用道具 举报

ID:570093 发表于 2021-5-28 16:01 | 显示全部楼层
wulin 发表于 2021-5-28 15:27
0~9数字转字符ASCII码+48(0x30),同理0~9字符转数字-48(0x30)。

这个我知道,但是发送一串字符串的时候就得知道字符串长度,在keil中使用strlen()会报错说缺少函数原型
回复

使用道具 举报

ID:213173 发表于 2021-5-28 16:43 | 显示全部楼层
96317 发表于 2021-5-28 16:01
这个我知道,但是发送一串字符串的时候就得知道字符串长度,在keil中使用strlen()会报错说缺少函数原型

对于固定字节长度的字符串,这个问题很好处理。对于不固定字节长度的字符串,接收完成时就已知字节长度。如果程序无法知道什么时候接收完成就要开一个定时器中断,每接收一个字节定时器复位,在达到设定时间没有收到新的数据定时器中断发生,就以此判断为一帧数据接收完成。接收的字节长度当然就知道了。
回复

使用道具 举报

ID:570093 发表于 2021-6-13 15:54 | 显示全部楼层
参考了一个蓝牙发送的程序,经过测试可以用,比如串口发送“12”可以将其转成整型数据12,发送字符串“12345”可以转成整型数字12345。但是发现该程序跳出while()循环后想要使用发送过来的数据有点麻烦。
  1. #include<reg52.h>
  2. #include<intrins.h>
  3. #include <stdio.h>

  4. #define uchar unsigned char
  5. #define uint  unsigned int
  6. #define ulong  unsigned long

  7. uchar uart_receive_buffer[30];  //从串口接收的数据
  8. uint uart_receive_number=0;    //指示串口接收个数
  9. uchar receive_length;                   //串口发送字符串长度
  10. ulong str_to_int=0,int_bit=1;  //字符串转整型数值、int-bit用来做10的n次方
  11. sbit led=P2^0;

  12. void Delay_1ms(uint i)//1ms延时
  13. {
  14.   uchar x,j;
  15.   
  16.   for(j=0;j<i;j++)
  17.     for(x=0;x<=148;x++);
  18. }

  19. void Com_Int(void) interrupt 4
  20. {
  21.   EA = 0;
  22.   if(RI == 1)
  23.   {  //当硬件接收到一个数据时,RI会置位
  24.     RI = 0;
  25.     uart_receive_buffer[uart_receive_number] = SBUF;  //这里减去48是因为从电脑中发送过来的数据是ASCII码。
  26.     uart_receive_number++;
  27.   }
  28.   EA = 1;
  29. }

  30. void Com_Init(void) {
  31.   TMOD = 0x20;
  32.   PCON = 0x80;          //波特率加倍
  33.   SCON = 0x50;                                                                  
  34.   TH1=0XF3;                                //计数器初始值设置,注意串口软件波特率是4800的
  35.   TL1 = 0XF3;
  36.   TR1 = 1;  //启动定时器1
  37.   ES = 1;  //开串口中断
  38.   EA = 1;  //开总中断
  39. }

  40. void PostChar(uchar character)  
  41. {     
  42.             SBUF=character;   //发送单个字符
  43.             while(!TI);           // 等待发送数据完成发送完成TI会置1
  44.                 TI=0;                     //发送完成标志
  45. }



  46. void Main()
  47. {
  48.   uchar uart_receive_number_old=0;
  49.   uchar i=0,j;
  50.   Com_Init();
  51.   
  52.   while(1)
  53.   {
  54.                                                                                                                 
  55.     if(uart_receive_number_old!=uart_receive_number)
  56.         {
  57.       uart_receive_number_old=uart_receive_number;
  58.     }
  59.    
  60.     //如果单片机接收来自HC-xx模块不少于1字节的串口数据,一定要加延时后再判断是否
  61.     //还有串口数据在发送,这样才能完整的接收一帧串口数据
  62.     Delay_1ms(20);
  63.    
  64.     //一段时间之后仍然相等,表示上位机发来的一串数据结束
  65.     if(uart_receive_number_old==uart_receive_number)
  66.         {
  67.       if(uart_receive_number)                 
  68.                 break;      
  69.     }
  70.   }
  71.   
  72. if(uart_receive_number)
  73. {
  74.         //led=~led;         //测试
  75.         receive_length=uart_receive_number;

  76.        
  77.         /*if(receive_length==6)         //////测试串口接收位数,串口软件如果发送字符串1234,则接收位数为4
  78.                 led=~led;                                         */

  79.         uart_receive_number=0;
  80.     //串口有数据时,该做些什么就做什么吧
  81.         for(i=0;i<receive_length;i++)
  82.         {       
  83.                 PostChar(uart_receive_buffer[i]);//将接收到的数据放入到发送寄存器
  84.                 //Delay_1ms(10);                        //延时,作用为提高发送准确度,可适当调节延时时间
  85.                
  86.                 int_bit=1;
  87.                 for(j=receive_length-i-1;j>=1;j--)
  88.                         int_bit*=10;       
  89.                 str_to_int += (uart_receive_buffer[i]-'0')*int_bit;       
  90.                
  91.                                        
  92.         }
  93.        
  94.         //if(str_to_int==1000000)                //串口软件发送的字符串要跟这里整形数据一样才行,不然发送一次后悔一直发送且显示不正常,暂时找不出原因
  95.         //        led=~led;
复制代码



回复

使用道具 举报

ID:937124 发表于 2021-6-13 17:38 | 显示全部楼层
传输数字,可以吧数字拆分成字节,然后按顺序传输;收到后再恢复就可以了;
回复

使用道具 举报

ID:937124 发表于 2021-6-13 17:40 | 显示全部楼层
一般数字,会是16位,32位,或更多位;只要拆封成8位一组,八位八位的传过去就可以了;因为51,收发寄存器是8位的;
回复

使用道具 举报

ID:261837 发表于 2021-6-14 13:33 来自手机 | 显示全部楼层
加帧头帧尾
回复

使用道具 举报

ID:883031 发表于 2021-6-15 09:30 | 显示全部楼层
接收到字符是ASCII码,数字的话减掉30就是对应的数字。如:1的ASCII码是31,减掉30就可以了。注意这里是16进制。
回复

使用道具 举报

ID:844772 发表于 2021-6-15 11:11 | 显示全部楼层
96317 发表于 2021-6-13 15:54
参考了一个蓝牙发送的程序,经过测试可以用,比如串口发送“12”可以将其转成整型数据12,发送字符串“1234 ...

有些常识还是要知道啊,比如51的int最大65535,不会等于你的1百万的。还有不控制程序,它会跑飞的,所以都会让它进死循环,但你让它跳出了。
回复

使用道具 举报

ID:81196 发表于 2021-6-15 13:37 | 显示全部楼层
对于单片机,不管你是传输数字还是字符,其传输过程中都是16进制表示的.只是在你接收时解析或发送时转义表示成你的内容对应的十六进制数据即可
回复

使用道具 举报

ID:811711 发表于 2021-6-16 11:00 | 显示全部楼层
首先要建议你多看看计算机组成原理,想不出来答案是因为缺少对计算机最本质的理解。
回复

使用道具 举报

ID:811711 发表于 2021-6-16 11:01 | 显示全部楼层
其次,你可以自己定义一个数据长度,标志位,或者还有别的思路可以解决这个问题但是还是建议多从计算机思维出发,计算机啥也不是只会遵守规则。
回复

使用道具 举报

ID:811711 发表于 2021-6-16 11:03 | 显示全部楼层
数字和字符都是同一种东西啊,只是你翻译他的方式不一样
回复

使用道具 举报

ID:880574 发表于 2021-11-14 12:01 | 显示全部楼层
lzl12399 发表于 2021-5-28 08:57
#include

//------------------串口通信的数据包协议-----------------//

这个里面的buf_string没有定义啊
回复

使用道具 举报

ID:941439 发表于 2021-11-18 20:49 | 显示全部楼层
我建议你可以把要传输的字符串数据先转换成int型数据,然后串口发送出去应该就可以了
回复

使用道具 举报

ID:897866 发表于 2021-11-27 18:43 | 显示全部楼层
可以参考0~9数字转字符ASCII码+48(0x30),同理0~9字符转数字-48(0x30)。
回复

使用道具 举报

ID:1011216 发表于 2022-4-9 23:48 来自手机 | 显示全部楼层
我来说说我的思路,上位机把整型数据当做两个字节发送给单片机,这个其实很好实现,在单片机端用左移八位命令把两个字节分开,然后合并成原整数,通过实践,低字节的数据是没问题的,高字节怎么都不对。单片机做数据接收太不稳定了,不知有啥办法可以解决这个数据稳定的问题
回复

使用道具 举报

ID:625730 发表于 2022-4-10 11:29 | 显示全部楼层
xinwenhyan 发表于 2022-4-9 23:48
我来说说我的思路,上位机把整型数据当做两个字节发送给单片机,这个其实很好实现,在单片机端用左移八位命 ...

上位机发的都是8位的字符,比如你发12,就是发1和2的字符,单片机接收了以后,要转换成数字1和2,然后1乘以10,再加2,得到12,要对照ascii码表进行转换。
回复

使用道具 举报

ID:1017168 发表于 2022-4-10 11:47 | 显示全部楼层
在数据的末尾减去"\0",将对应的字符ascii码转换成数字
回复

使用道具 举报

ID:480627 发表于 2022-4-11 15:35 | 显示全部楼层
基础知识不足的弊端啊。。。。什么是二进制,什么是十六进制,怎么用,平时单片机或电脑又是怎么处理数据的。。。
回复

使用道具 举报

ID:1009420 发表于 2022-4-11 16:32 | 显示全部楼层
16位,32位,或更多位;只要拆封成8位一组,八位八位的传过去就可以了
回复

使用道具 举报

ID:982617 发表于 2022-4-13 17:38 | 显示全部楼层
先把数据类型都统一了吧
回复

使用道具 举报

ID:593706 发表于 2022-4-14 08:43 | 显示全部楼层
用字节发送数组就可以
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表