打印

急啊!单片机串口通信程序问题!(6.4更新中)

[复制链接]
楼主: enjoy21cn
手机看帖
扫描二维码
随时随地手机跟帖
21
刘前辈| | 2009-6-1 09:58 | 只看该作者 回帖奖励 |倒序浏览

突然悟出了一个道理:生产者——消费者关系。

“这样的用法显然可以主程序一边向发送缓存区里置放欲发送的数据,中断发送程序又可以一边同时进行发送,100字节长的发送帧从数据区到16字节的发送缓存区没有出现等待延迟。”

*******************************************************************

这实际上是操作系统概念中生产者—消费者的关系:主程序是生产者—它生产数据到环形发送缓存区,UART中断发送程序是消费者,——它消费环形发送缓存区的数据。

如果良性循环,则一边主程序生产数据,一边自动消费数据,很小的缓存区就
可以满足需要。只有在“供大于求”的情况下,因数据包太大,缓存区太小而出现生产者被迫等待。

19楼说得好,编程员不一定要在裸奔中采用OS的概念方法,能够简单就简单。简单才是最好。所以最好能有个人把裸奔中断发送程序写一个完善的版本,供大家参考、裁剪、使用。也可以成为标准。


这算是C语言的初衷?:有一个人做过了,大家都参考他的模块就行了。就好像printf( )一样调用。

如果所有人都要经过像LZ一样的自己设计、独立思考、再推翻重来这样的阶段...,每个人都要把中断发送程序再做、再调试一遍,千人千面,互不参考,那可太浪费生命时光了。

使用特权

评论回复
22
刘前辈| | 2009-6-1 10:45 | 只看该作者

暂时小改动。

最后一个串:uchar h[4]={'B','V','4','0'};

修改为:
uchar h[]="BV40\r";


待续:大改动。

使用特权

评论回复
23
刘前辈| | 2009-6-1 17:29 | 只看该作者

不可能错位。

#include <reg52.h>
 #include <stdio.h>


code char disp[]=
"\n"
"/****************************************************************************/\n"
"/*                                                                          */\n"
"/*    晶振:11.0592MHZ                             CPU型号:AT89S52         */\n"
"/*                                                                          */\n"
"/*       功能描述    通过串口间隔一定时间输出一系列的指令                 */\n"
"/*                                                                          */\n"
"/*                 CIS SK530 FD5000 TW GX45 MA200000 L000000000  BV40.      */\n"
"/*                                                                          */\n"
"/****************************************************************************/\n" ;  

 

void uini()   
{
  TMOD=0x20;
  TH1=0xfd;//波特率为9600
  TL1=0xfd;
  TR1=1;
  SCON=0x50;
}


void main()
{
  uini();
printf(disp);
  while(1);

}

使用特权

评论回复
24
mohanwei| | 2009-6-1 18:57 | 只看该作者

参考Keil自带的hello,world例程就知道一般怎么写了

使用特权

评论回复
25
longdouble| | 2009-6-1 23:16 | 只看该作者

这个帖子很好对新手很有指导意义

教科书,哪里会将这些。
都是按照楼主的做法做的。所以你说现在学校出来的人还要走多少路才能有自己的思想。

使用特权

评论回复
26
highgear| | 2009-6-2 00:43 | 只看该作者

一个简单的问题怎么被弄得如此复杂?

什么生产者,消费者,环形送缓存区都冒出来了。

void SendDataViaSerialPort(char* buffer, char length)
{
    char i;
    for (i=0; i<length; i++) {
        SBUF = *buffer++;
        while (! TI);
        TI=0;
    }
}

使用:
SendDataViaSerialPort(a, sizeof(a));
SendDataViaSerialPort(b, sizeof(b));

也可以像 16楼handrap 那样使用中断:
typedef struct SSerialTransfer
{
    char*     buffer;
    char    length;    

}  SerialTransfer;


void SendDataViaSerialPort(char* buffer, char length)
{
    tx.buffer = buffer + 1;
    tx.lenght = length;
    SBUF = *buffer;
}

void serial_int(void) interrupt 4 using 2
{
    if (RI) { }
    if (TI) {
        TI = 0;
         tx.length--;
        if (tx.length != 0) {
             SBUF = *tx.buffer++;
        }
    }
}

使用:
SerialTransfer tx;

SendDataViaSerialPort(a, sizeof(a));
while (tx.length != 0); //waiting for complete

使用特权

评论回复
27
enjoy21cn|  楼主 | 2009-6-2 11:40 | 只看该作者

又添加了程序

这个程序加了一段发送回车键0x0D的程序,
void SendEndChar(void)
{
    SBUF=0x0D;
while(TI==0){ }
TI=0;
}
现在可以发送数据了,但为什么驱动那边还是不识别呢?
请高手指点一下。

使用特权

评论回复
28
刘前辈| | 2009-6-2 11:55 | 只看该作者

老所长的思维太退化了,把这么简单的事情复杂化。

果然是“天下第一”写的程序。写这么长,只能自我欣赏。关键是,只能给各位参考,能不能运行那就要看各位的造化了。

瞪大眼睛再仔细看看LZ的题目和下面的程序, 1 条语句就实现啦!还等您老人家慢慢思维程序结构?咱早拿了银子走人干别的去了。

仔细想想自己的程序有没有什么“脱.....——多此一举”的地方。

修炼了半辈子,堂堂 R&D 大堂主写程序就这水平?C语言基本概念上还不如我一个初学者。

要是看不出来自己的缺陷,小心一帮学生又要哄笑“下课”啦。


 #include <stdio.h>

code char disp[]="  CIS SK530 FD5000 TW GX45 MA200000 L000000000  BV40. \r ";


void main()
{
puts(disp);

 while(1);

}




Build target 'Target 1'
compiling Pointer.c...
linking...
Program Size: data=9.0 xdata=0 code=188
"test" - 0 Error(s), 0 Warning(s).

使用特权

评论回复
29
徐小剑| | 2009-6-2 12:21 | 只看该作者

会不会是烧录是选择的串口不对呀!!1

会不会是烧录时候选择的烧录串口不对呀!!!
我是新手,刚刚看是玩单片机的!!

使用特权

评论回复
30
yzhj| | 2009-6-2 13:26 | 只看该作者

这个要看具体的应用了

如果对资源相对宽松,实时响应不是很严格。我还是喜欢把要发送的数据全部处理好,送到发送缓冲数组,再开始发送。
这样做看起来似乎“很笨”,但是对于程序的可读性和日后的维护非常有好处。

使用特权

评论回复
31
zyboy| | 2009-6-2 14:39 | 只看该作者

highgear和刘前辈都没错,没必要对掐呀,哈哈

我在程序里面常用的两种方法:
一种查询方式:就说highgear的第一种方法
/********************************************************************************
* Function Name  : SendCommChar1
* Description    : 串口1发送多个字节(查询方式)
* Input          : senddata:要发送的数据的指针   length:要发送数据的长度
* Output         : None
* Return         : None
*******************************************************************************/
void SendCommReqStr1(unsigned char xdata *senddata,int length)
{
    int m;
    EA = 0;

    ClearTI1;    
    for(m=0;m<length;m++)
    {
        SBUF1 = *(senddata+m);           //send DATA
        while(TI1 == 0); 
        ClearTI1;
    }

    EA = 1;

}

一种中断发送,不过采样队列方式"刘前辈所说的",主程序只管往队列丢数据,中断自己从队列取
 /********************************************************************************
* Function Name  : SendCommBuffer0
* Description    : 发送多个字节 (队列方式)
* Input          : base:数据指针  size:数据长度
* Output         : None
* Return         : None
*******************************************************************************/
void SendCommBuffer0(unsigned char *base, unsigned char size) 
{
    unsigned char i=0;
    if (!size) { return; }    
    while (i<size) 
    {     
        g_CommSendBuffer0[g_Sendtail0]=base
        i++;
        g_Sendtail0++; 
        if (g_Sendtail0==DB_SENDMAXSIZE0)
        { 
            g_Sendtail0=0;
        }
    }
    if (g_SendItComm0)
    {     
        SBUF0=g_CommSendBuffer0[g_Sendhead0]; 
    }
}


/********************************************************************************
* Function Name  : CommISR0
* Description    : 串口0中断
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void CommISR0(void) interrupt 4
{
//    EA=0;
    if (_testbit_(TI0))
    {

        TI0=0;
        g_Sendhead0++;     
        if (g_Sendhead0==DB_SENDMAXSIZE0)
        {     
            g_Sendhead0=0;
        }
        if (g_Sendhead0!=g_Sendtail0)
        {     
            SBUF0=g_CommSendBuffer0[g_Sendhead0]; // send the next byte
            g_SendItComm0=0;
        }
        else
        {
            g_SendItComm0=1;
        }
    }
    if (_testbit_(RI0))    
    {     
        RI0=0;
    
        g_CommRecBuffer0[g_Rectail0]=SBUF0;     //receive data           
        g_Rectail0++;
        if (g_Rectail0==DB_RECMAXSIZE0)
        {
            g_Rectail0=0;
        }
        g_FlagRecComm0=1;
       }
//    EA=1;
}
很难说那种方法好,那种方法好,那种坏:
看你的需求了。队列保证了数据包的最大完整性,尽可能的不丢失包,却失去了实时性。

上班等着下班,好无聊呀,打酱油的日子也不好过呀

使用特权

评论回复
32
enjoy21cn|  楼主 | 2009-6-2 15:31 | 只看该作者

添加了一段发送回车的函数

这个程序加了一段发送回车键0x0D的程序,
void SendEndChar(void)
{
    SBUF=0x0D;
while(TI==0){ }
TI=0;
}
目前在串口调试中发现一个奇怪的问题
手册要求的发送命令格式:
指令+参数+0x0D

实际操作中的发送格式是:
指令+参数+(直接在键盘上回车)
+0x0D
难道要两次回车一个是隐性的,一个显性的。
迷惑中?
这个是不是可以在发送两次 SendEndChar();
在程序中发送了两次居然也没回应!
有没有法子解决这个问题呢?

使用特权

评论回复
33
刘前辈| | 2009-6-2 19:25 | 只看该作者

先向highgear道个歉,26楼的程序当然可用。可惜...

大堂主大概忽视了一个重要的事情:LZ的要求是处理“字符串”,没问你uchar通用发送程序怎么写。通用发送程序书上到处都有,都比咱们写得好。就不用在这显示天下第一创新程序了吧。

C语言字符串处理有何特点?下面disp[ ]字符串的长度是多少?还用得着大堂主在这sizeof(a)求length? “多此一举”——C语言初学者!

就这水平“天下第一”到处找人PK?

看看下面咱菜鸟怎么给高手修改程序。——见笑了。
(咱puts(disp)/printf(disp) 仅一条语句就完成了。还得给高手讲述/修改C语言中字符串处理概念。)


 #include <stdio.h>


code char disp[]=" CIS SK530 FD5000 TW GX45 MA200000 L000000000  BV40. \r ";


void SendDataViaSerialPort(char buffer[])     //只需要一个参数就够了。
{
    //char i;
    for ( ; *buffer!='\0'; )        // 看看for的另类用法。高手只知道for(i=0; i<x;i++ )?
    {
        SBUF = *buffer++;
        while (! TI);
        TI=0;
    }
}



void main()
{
SendDataViaSerialPort(disp);

  while(1);

}

*********************************************************
对照原版,这是大堂主写的字符串发送程序?和普通发送程序没什么两样,太寻常了。

void SendDataViaSerialPort(char* buffer, char length)
{
    char i;
    for (i=0; i<length; i++) {
        SBUF = *buffer++;
        while (! TI);
        TI=0;
    }
}

使用:
SendDataViaSerialPort(a, sizeof(a));

使用特权

评论回复
34
highgear| | 2009-6-2 21:02 | 只看该作者

刘公公,你若不服气,就PK.

先把前面的 PK 了结,再 PK 一个难度大一些的字符处理的程序,如何?
我告诉你刘公公无数遍了,技术这东西,懂就是懂,不懂就不要装懂。

“太寻常了“,没错,这个东西非常简单,所以我不明白你刘公公为什么拉拉撒撒的一堆,也没有说明白。

另外:不是我要找你刘公公PK,是你你刘公公先提出 PK, 我被迫迎战,结果呢?你刘公公太监了,哈哈,无胆鼠辈!亏你刘公公还有脸皮在这里“看看下面咱菜鸟怎么给高手修改程序“。

再教育你一次:(我已经教育你刘公公多次了,你是不是该交点学费了?)
1) 只要传入一个buffer, 一定要同时规定size, 这是菜鸟与高手的区别之一
2) 一个函数应当是自恰的,鲁棒的,这也是菜鸟与高手的区别之一
3) 关于判断 '\0', 这是一个相当简便但非常糟糕的方法。为什么糟糕?请去看看 msdn。visual studio 已经对以前不使用size,而是判断'\0'的基本函数都会给出一个 warning,现代程序的崩溃大多源于此。著名的溢出式攻击更是利用判断'\0'。
4)在mcu下,判断'\0'的效率并不比最终的 djnz 循环高,更多的时候是更低。

 
同时:
顶 zyboy,程序作的很好。   










使用特权

评论回复
35
冷漠| | 2009-6-2 22:08 | 只看该作者

哈哈,所长果然天下第一!

前辈的程序是抄的书上的。这本书的名字世界流传——《POINTERS ON C》。咱中国多少人以它为范本《C和指针》。

老所长如今精神上越来越孤芳自赏,连Kenneth都不放在眼里啦?自恋型人格障碍的典型症状就是“事事要和别人比较”,一旦感觉他人超越了自己就妒火中烧;把别人贬的一文不值,他就开心了。精神病状啊。

这也没什么,不影响别人就行了。咱不和有人格障碍的人PK,输给学长4回了,没事人一样,又来啦。到底还要输几会才算是认输?如今只能找菜鸟PK啦?赢了刘前辈他就开心了?前辈出的第一道题,大堂主视而不见?装傻?那还玩什么,要不要我在这重复一遍?

字符串必以‘\0'结尾,为了防止字符串经过多种串函数处理之后\0标记丢失,可以用 buffer[BSISE-1]='\0'; 来确定字符串实际上是以NUL字节结尾。 之所以一定要这样做,是因为几乎所有字符串处理库函数,都是利用NUL标记识别字符串结尾的。

呵呵,大堂主谬论:“字符串一定要求出长度....”,也难怪老所长一个字符串库函数都不会用呢。

咱再请问了:我的程序中,puts(disp)和printf(disp)用到字符串长度了?不好解释了。

辩论无用。不服Kenneth,老所长您自己也可以写一本关于字符串处理函数什么的,说不定也能流传世界,轰动斯德哥尔摩啊。

在这讲课只能唬我们菜鸟。欺负咱没看过书的人,哈哈。

使用特权

评论回复
36
szsfy| | 2009-6-2 22:19 | 只看该作者

哈哈

使用特权

评论回复
37
冷漠| | 2009-6-2 22:21 | 只看该作者

前辈的程序好精彩,不过是参考了书上的,无懈可击。

请问天下第一老所长:disp[]的长度是多少?别客气。——根本用不着。没有长度信息,看来outs()和printf()函数都不能用了?谁用谁错。只有用咱们“天下第一”老所长的程序才是正根儿。

#include <stdio.h>

code char disp[]="  CIS SK530 FD5000 TW GX45 MA200000 L000000000  BV40. \r ";


void main()
{
puts(disp);

while(1);

}


使用特权

评论回复
38
highgear| | 2009-6-2 23:40 | 只看该作者

冷嬷嬷迫不及待的跳出来了。

PK 中你们太监了,心里即不服气,对吧?不服气就PK。

刘公公冷嬷嬷,你们无法辩驳 34 楼里的'\0'的解释,胡搅蛮缠也解决不了你们水平的不足。

呵呵,我本不想解释"\0", 本想像上次(char) i 那样看你表演,不过后来想想还是厚道些。我不想再解释microsoft 关于'\0'的结论,一句话: 理论上没错,实践中有问题(puts, printf 等不像strcpy, scanf那样,一般被认为是safe,但是事情总有例外......)。刘公公冷嬷嬷试图以此来攻击,哈哈,当真可笑。不服?刘公公冷嬷嬷可以PK啊。刘公公冷嬷嬷再不服,还可以去挑战 microsoft 啊. 

另外,《POINTERS ON C》是一本不错的教科书,1997年出版,“世界流传“就免了吧。amazon 上评价可以 (http://www.amazon.com/Pointers-C-Kenneth-Reek/dp/0673999866)。
这个评论更贴切:
If you have never programmed this is not a book for you. If you are already a competent C programmer, look elsewhere. (http://www.accu.informika.ru/accu/bookreviews/public/reviews/p/p001566.htm)


“字符串必以‘\n'结尾“,扯蛋,我再告诉你们东厂太监们一遍,技术这东西,懂就是懂,不懂就不要装懂。“确定字符串实际上是以NUL字节结尾“,字符串甚至可以不是以0结尾(呵呵,再给东厂太监们一个发挥的机会)。

“字符串一定要求出长度....”,又祭出你们东厂太监的法宝:诬蔑造谣**。如果你冷嬷嬷如果找不出我说过这句话,你冷嬷嬷敢不敢自宫?




  

使用特权

评论回复
39
highgear| | 2009-6-3 00:41 | 只看该作者

Kenneth A. Reek 这句话很有意思:

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

我还是厚道些吧,多说一些技术道理。赢了两个菜鸟tj 也不是什么光彩的事。

34楼中的 1)给初学者做个解释:
f(char* buffer, int size) 必须有意这么做,目的是强迫使用f者去考虑buffer大小,这能够在很大程度上抑制buffer overflow 的问题,特别是对于团队合作的项目。初学者往往注重微观,而忽略了宏观上的问题。


 

使用特权

评论回复
40
zyboy| | 2009-6-3 08:41 | 只看该作者

highgear说的不错,但没必要对掐了,呵呵,说他们是“菜鸟"

highgear说的不错

void SendDataViaSerialPort(char buffer[])     //只需要一个参数就够了。
{
    //char i;
    for ( ; *buffer!='\0'; )        // 看看for的另类用法。高手只知道for(i=0; i<x;i++ )?
    {
        SBUF = *buffer++;
        while (! TI);
        TI=0;
    }
}
这样写太危险,万一buffer中没有'\0'呢!虽然传入的是"**",但显然传单个字符数组也可以传递参数(不带\0),这样无法保证一定是字符串传入。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则