当前位置:文档之家› ZigBee串口收发数据

ZigBee串口收发数据

ZigBee串口收发数据
ZigBee串口收发数据

本文转载自:https://www.doczj.com/doc/a5263798.html,/space.php?uid=20788636&do=blog&id=1841411

串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。这里以SerialApp例程为例子。在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA 通道

void HalDriverInit (void)

{

...................................

#if (defined HAL_UART) && (HAL_UART == TRUE)

HalUARTInit();

#endif

....................................

}

从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UART TRUE 在hal_board_cfg.h文件中。

#ifndef HAL_UART

#if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)

#define HAL_UART TRUE

#else

#define HAL_UART FALSE

#endif

#endif

然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。

在CC2430的数据手册中有这样一段话。

Data reception on the UART is initiatedwhen a 1 is written to the UxCSR.RE bitThe UART will then search for a valid start bit on the RXDx input pin and set theUxCSR.ACTIVE bit high. When a validstart bit has been detected the received byte is shifted into the receive register .The UxCSR.RX_BYTE bit is set and a receive interrupt is generated when the operation has completed. The received data byte is available through the UxBUF register. When UxBUF is read, UxCSR.RX_BYTE is cleared by hardware.

当有数据接收时,UxCSR.RE位将被置1,然后,UART将在RXDx的输入引脚上查找一个有效的开始位,当找到这个开始位时,将设置UxCSR.ACTIVE位为高电平。当一个有效的开始位被查找到,收到的字节将被移动到接收寄存器中。然后,UxCSR.RX_BYTE位设为1.并且,当这个接收操作完成后接收中断会被产生。接收到的数据可以通过操作UxBUF 寄存器,当UxBUF寄存器的数据被读出后,UxCSR.RX_BYTE位被硬件清除。串口发生中断首先调用中断的处理函数,这个是接收的中断函数。

#if HAL_UART_0_ENABLE

HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )

{

cfg0->rxBuf[cfg0->rxHead] = U0DBUF;

if ( cfg0->rxHead == cfg0->rxMax )

{

cfg0->rxHead = 0;

}

else

{

cfg0->rxHead++;

}

}

#endif

该中断函数主要是把U0DBUF寄存器,也就是接收到数据的寄存器,把数据读取来放到UART的结构体中的,cfg0->rxBuf[],中,这个数组的内存分配是在HalUARTOpen()函数中。SerialApp.c中有下面的定义

#if !defined( SERIAL_APP_RX_MAX )

#if (defined( HAL_UART_DMA )) && HAL_UART_DMA

#define SERIAL_APP_RX_MAX 128

#else

#define SERIAL_APP_RX_MAX 64

#endif

#endif

SerialApp_Init()函数中有下面的赋值,

uartConfig.rx.maxBufSize = SERIAL_APP_RX_MAX;

HalUARTOpen()函数中有下面的赋值:所以其cfg->rxMax=128,

cfg->rxMax = config->rx.maxBufSize;

其中rxHead这个参数始终指向像一个参数被存放到rxBuf的位置。因为硬件串口缓存器U0DBUF只能存放一个字节,如果不及时把这个接收到的转移出去,那么就会被下一个到来的字节覆盖掉,所以rxHead变量就指向了这个存放的地址,当然是基于定义的rxBuf存储空间。

而if ( cfg0->rxHead == cfg0->rxMax )这一句判断也说明的很清楚,一旦这个计数达到了定义的最大接收数量,也就是说已经把rxBuf存储空间占满了,那么就不能在继续存放了。

中断函数执行完后,就应该跳到发生中断时执行的地方,这时程序继续执行,然后在osal_start_system()开始系统后,会循环调用Hal_ProcessPoll()来读取时间和串口,

void Hal_ProcessPoll ()

{

HalTimerTick();

#if (defined HAL_UART) && (HAL_UART == TRUE)

HalUARTPoll();

#endif

}

下面是HalUARTPoll();函数的源代码,在这里有对接收到的数据进行处理的程序。

void HalUARTPoll( void )

{

#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE )

static uint8 tickShdw;

uartCfg_t *cfg;

uint8 tick;

#if HAL_UART_0_ENABLE

//当发生串口接收中断时cfg0就会改变,如果串口没有数据输入cfg0为空,当接收到数据时cfg0将在串口中断服务程序中被改变

if ( cfg0 )

{

cfg = cfg0;

}

#endif

#if HAL_UART_1_ENABLE

if ( cfg1 )

{

cfg = cfg1;

}

#endif

// Use the LSB of the sleep timer (ST0 must be read first anyway).

//系统上电后,睡眠定时器就会自动启动做自增计数ST0即睡眠定时器启动到现在计算值的最低8位

tick = ST0 - tickShdw;

tickShdw = ST0;

//下面是一个无限循环

do

{

//------------发送超时时间

if ( cfg->txTick > tick )

{

cfg->txTick -= tick;

}

else

{

cfg->txTick = 0;

}

//---------------------接收超时时间

if ( cfg->rxTick > tick )

{

cfg->rxTick -= tick;

}

else

{

cfg->rxTick = 0;

}

//是使用DMA方式还是使用中断方式

#if HAL_UART_ISR

#if HAL_UART_DMA

if ( cfg->flag & UART_CFG_DMA )

{

pollDMA( cfg );

}

else//中断方式

#endif

{

pollISR( cfg );

}

#elif HAL_UART_DMA

pollDMA( cfg );

#endif

if ( cfg->rxHead != cfg->rxTail ) //不相等表示有数据

{

uint8 evt;

if ( cfg->rxHead >= (cfg->rxMax - SAFE_RX_MIN) )

{

//已保存的数据已经超过了安全界限,发送接收满事件

evt = HAL_UART_RX_FULL;

}

else if ( cfg->rxHigh && (cfg->rxHead >= cfg->rxHigh) )

{

//rxBuf[ ]接收到预设值(默认80字节),则触发事件,为什么是80,在上一篇转载的文章中有介绍,这里重点关注执行的流程。

evt = HAL_UART_RX_ABOUT_FULL;

}

else if ( cfg->rxTick == 0 )

{

//超时事件

evt = HAL_UART_RX_TIMEOUT;

}

else

{

evt = 0;

}

//如果发生事件,并且配置了回调函数则调用回调函数

if ( evt && cfg->rxCB )

{

//(cfg->flag & UART_CFG_U1F)!=0)判读是那个串口,如果是串口1则为1,否则为0 cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );

}

}

#if HAL_UART_0_ENABLE

if ( cfg == cfg0 )

{

#if HAL_UART_1_ENABLE

if ( cfg1 )

{

cfg = cfg1;

}

else

#endif

break;

}

else

#endif

break;

} while ( TRUE );

#else

return;

#endif

}

说明:(1)下面我们看一下pollISR()函数

static void pollISR( uartCfg_t *cfg )

{

//计算rxBuf[]中还有多少数据没有读出(以字节为单位)

uint8 cnt = UART_RX_A V AIL( cfg );

//如果串口没有接收到数据,也就是说没有发生过串口接收中断,那么cfg应为是为空的,则cnt=0如果发生了串口中断,则cnt计算出串口缓存中还有多少数据没有读出,这个缓存并不是硬件寄存器的缓存,而是程序中开辟一段空间

if ( !(cfg->flag & UART_CFG_RXF) )

{

//这里是针对流控制的,如果又有新的数据接收到了那么就要重置超时时间(超时时间由睡眠定时器来控制),而且需要把已经读出的数据数目减去!

// If anything received, reset the Rx idle timer.

if ( cfg->rxCnt != cnt )

{

cfg->rxTick = HAL_UART_RX_IDLE;

cfg->rxCnt = cnt;

}

if ( cfg->rxCnt >= (cfg->rxMax - SAFE_RX_MIN) )

{

RX_STOP_FLOW( cfg );

}

}

}

#endif

pollISR()函数主要作用就是设置rxTick和rxCn,

//关于安全界限,在程序中有下面一段:

//如果声明了流控制,为保证数据的正确接收需要在RX缓存区中预留出足够的空间。CC2430可以使用的最大串口波特率为115.2k。这个安全界限的数字跟使用的波特率还有串口tick 有关。具体参考《Z-STACK问题之串口结构uartCfg_t乱说》文章。

可以看到,在初始化时rxHead=rxTail=0,如果发生接收中断,在中断服务函数中把U0DBUF寄存器中的数据传送到rxbuf[]中,这时rxHead和rxTail的值不在相等,其中,rxHead 是rxBuf[]接收到数据的个数,rxTail是rxBuf[]移出的数据个数,再根据两者的差值,判断具体的事件evt。然后,根据evt和设置的回调函数,通过cfg->rxCB调用相应的回调函数。代码也是体显在下面一句。

cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );

第一个参数主要是判断,是UART1还是UART0.第二个参数是触发的事件类型,那个个回调函数,具体是指向函数呢?

首先,我们在void SerialApp_Init( uint8 task_id )初始化函数中,对串口进行了配置,其中下面两句中有关于回调函数的。

#else

uartConfig.callBackFunc = rxCB;

#endif

HalUARTOpen (SERIAL_APP_PORT, &uartConfig);

其中,在HalUARTOpen()函数中,有下面的一条语句,

uint8 HalUARTOpen( uint8 port, halUARTCfg_t *config )

{

...................

cfg->rxCB = config->callBackFunc;

...................

}

也就是调用下面的rxCB函数。程序中定义了两个串口接收缓冲区:otaBuf上otaBuf2.当otaBuf中无数据时,处于空闲状态时,由otaBuf接收串口数据;当otaBuf中保留有数据时,下等待接收节点发送接收数据响应或由于某些正在重新给接收节点发送数据时,可通过otaBuf2接收数据,当otaBuf和otaBuf2都没有处于空闲状态时,说明数据没有及时发送给接收节点,发生了数据累积,缓冲区被占用,需要进行流量控制,所以直接退出接收回调函数,暂不接收数据。

static void rxCB( uint8 port, uint8 event )

{

uint8 *buf, len;

if ( otaBuf2 ) //缓冲区被占用

{

return;

}

if ( !(buf = osal_mem_alloc( SERIAL_APP_RX_CNT )) )

{

return;

}

len = HalUARTRead( port, buf+1, SERIAL_APP_RX_CNT-1 );

if ( !len ) // Length is not expected to ever be zero.

{

osal_mem_free( buf );

return;

}

if ( otaBuf ) //otaBuf正在被占用

{

otaBuf2 = buf; //otaBuf2接收数据

otaLen2 = len;

}

else

{

otaBuf = buf; //otaBuf接收数据

otaLen = len;

osal_set_event( SerialApp_TaskID, SERIALAPP_MSG_SEND_EVT ); }

}

#endif

在事件处理函数中,有下面的判断。

UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )

{

........................

if ( events & SERIALAPP_MSG_SEND_EVT )

{

SerialApp_SendData( otaBuf, otaLen );//

return ( events ^ SERIALAPP_MSG_SEND_EVT );

}

.........................

}

下面是SerialApp_SendData()函数的源代码,调用AF_DataRequest(),通过OTA发送数据。由于在数据包之前增加了序列号SerialApp_SeqTx,多次重发的数据不会被接收节点重复发送到串口。

static void SerialApp_SendData( uint8 *buf, uint8 len )

{

afStatus_t stat;

// Pre-pend sequence number to the start of the Rx buffer.

*buf = ++SerialApp_SeqTx;

otaBuf = buf;

otaLen = len+1;

stat = AF_DataRequest( &SerialApp_DstAddr,

(endPointDesc_t *)&SerialApp_epDesc,

SERIALAPP_CLUSTERID1,

otaLen, otaBuf,

&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );

if ( (stat == afStatus_SUCCESS) || (stat == afStatus_MEM_FAIL) )

{

//在设定的时间内没有发送成功,则重新发送。

osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT,

SERIALAPP_MSG_RTRY_TIMEOUT );

rtryCnt = SERIALAPP_MAX_RETRIES;

}

else

{

FREE_OTABUF();//重发的次数

}

}

void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )

{

uint8 stat;

uint8 seqnb;

uint8 delay;

switch ( pkt->clusterId )

{

// A message with a serial data block to be transmitted on the serial port.

//接收节点收到的接收数据命令,

case SERIALAPP_CLUSTERID1:

seqnb = pkt->cmd.Data[0];

// Keep message if not a repeat packet

if ( (seqnb > SerialApp_SeqRx) || // Normal

((seqnb < 0x80 ) && ( SerialApp_SeqRx > 0x80)) ) // Wrap-around

{

// Transmit the data on the serial port.接收到的发送到串口

if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1,

(pkt->cmd.DataLength-1) ) )

{

// Save for next incoming message

SerialApp_SeqRx = seqnb;

stat = OTA_SUCCESS;

}

else

{

stat = OTA_SER_BUSY;

}

}

else

{

stat = OTA_DUP_MSG;

}

// Select approproiate OTA flow-control delay.

delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY;

// Build & send OTA response message. 发送响应消息

rspBuf[0] = stat;

rspBuf[1] = seqnb;

rspBuf[2] = LO_UINT16( delay );

rspBuf[3] = HI_UINT16( delay );

//发送接收数据响应命令

stat = AF_DataRequest( &(pkt->srcAddr),

(endPointDesc_t*)&SerialApp_epDesc,

SERIALAPP_CLUSTERID2,

SERIAL_APP_RSP_CNT ,

rspBuf,&SerialApp_MsgID, 0,

AF_DEFAULT_RADIUS );

if ( stat != afStatus_SUCCESS )

{

osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT,

SERIALAPP_RSP_RTRY_TIMEOUT );

// Store the address for the timeout retry. 存储发送超时的,目的地址

osal_memcpy(&SerialApp_RspDstAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));

}

break;

// A response to a received serial data block. 接收到接收数据响应命令

case SERIALAPP_CLUSTERID2:

if ( (pkt->cmd.Data[1] == SerialApp_SeqTx) &&

((pkt->cmd.Data[0] == OTA_SUCCESS) ||

(pkt->cmd.Data[0] == OTA_DUP_MSG)) ) //目的设备接收数据的状态{

// Remove timeout waiting for response from other device. 接收到返回的状态后,关闭定时器

osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT );

FREE_OTABUF(); //释放缓存区

}

else

{

delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );

// Re-start timeout according to delay sent from other device.

osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, delay );

}

break;

default:

break;

}

}

UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )

{

if ( events & SYS_EVENT_MSG ) //ZDO层接收到注册过的消息

{

afIncomingMSGPacket_t *MSGpkt;

while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(

SerialApp_TaskID )) )

{

switch ( MSGpkt->hdr.event )

{

case ZDO_CB_MSG:

SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );

break;

case KEY_CHANGE:

SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,

((keyChange_t *)MSGpkt)->keys );

break;

//接收到命令,然后执行,zigbee协议信息的传递有两种方式:消息和命令,消息长度不限,命令的大小则严格规定

case AF_INCOMING_MSG_CMD:

//执行发送过来消息命令的回调函数

SerialApp_ProcessMSGCmd( MSGpkt );

break

default:

break;

}

osal_msg_deallocate( (uint8 *)MSGpkt ); // Release the memory.

}

// Return unprocessed events

return ( events ^ SYS_EVENT_MSG );

}

//发送数据的事件,这里是串口通过CC2430发送数据到另一个设备

if ( events & SERIALAPP_MSG_SEND_EVT )

{

SerialApp_SendData( otaBuf, otaLen );

return ( events ^ SERIALAPP_MSG_SEND_EVT );

}

//重发数据的事件,如果发送数据没有成功的话

if ( events & SERIALAPP_MSG_RTRY_EVT )

{

if ( --rtryCnt )

{

AF_DataRequest( &SerialApp_DstAddr,

(endPointDesc_t *)&SerialApp_epDesc,

SERIALAPP_CLUSTERID1, otaLen,

otaBuf,

&SerialApp_MsgID, 0,

AF_DEFAULT_RADIUS );

osal_start_timerEx( SerialApp_TaskID,

SERIALAPP_MSG_RTRY_EVT,

SERIALAPP_MSG_RTRY_TIMEOUT );

}

else

{

FREE_OTABUF();

}

return ( events ^ SERIALAPP_MSG_RTRY_EVT );

}

//发送接收数据响应的重发事件

if ( events & SERIALAPP_RSP_RTRY_EVT )

{

afStatus_t stat = AF_DataRequest(

&SerialApp_RspDstAddr,

(endPointDesc_t *)&SerialApp_epDesc,

SERIALAPP_CLUSTERID2,

SERIAL_APP_RSP_CNT, rspBuf,

&SerialApp_MsgID, 0,

AF_DEFAULT_RADIUS );

if ( stat != afStatus_SUCCESS )

{

osal_start_timerEx( SerialApp_TaskID,

SERIALAPP_RSP_RTRY_EVT,

SERIALAPP_RSP_RTRY_TIMEOUT );

}

return ( events ^ SERIALAPP_RSP_RTRY_EVT );

}

#if SERIAL_APP_LOOPBACK

if ( events & SERIALAPP_TX_RTRY_EVT )

{

if ( rxLen )

{

if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )

{

osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,

SERIALAPP_TX_RTRY_TIMEOUT );

}

else

{

rxLen = 0;

}

}

return ( events ^ SERIALAPP_TX_RTRY_EVT );

}

#endif

return ( 0 ); // Discard unknown events.

}

在串口通信中设置了多个重发机制,增加数据通信的可靠性,首先是利用重发数据事件SERIALAPP_MSG_RTRY_EVT重发数据,重发的次数由rtyCnt设定。由于在数据包之前增加了序列号SerialApp_SeqTx,多次生发的数据不会被接收节点重复发送到串口。别外,加入了数据接收响应机制,发送节点在发送完数据后,等待接收节点返回接收数据响应,收到返回接收数据响应的命令SERIALAPP_CLUSTERID2:后,判断信息包中的接收状态参数。若接收状态为OTA_DUP_MSG,表明接收节点串口繁忙,应启动重发机制,延时后产生重发数据事件,若接收状态为OTA_SUCCESS,表明接收节点将数据成功发送到串口,就释放缓存区,等待串口接收下一包数据。

下面是串口的接受和发送的流程图:

串口接收、发送数据OK

//接收数据 procedure TFrm_https://www.doczj.com/doc/a5263798.html,m1ReceiveData(Sender: TObject; Buffer: PAnsiChar; BufferLength: Word); var i:integer; rbuf,sbuf:array[1..21] of byte; tmpstr,tmphex,tmpview:string; begin tmpstr:=''; tmphex:=''; move(buffer^,pchar(@rbuf)^,bufferlength); for i:=1 to BufferLength do begin tmpstr:=tmpstr+char(rbuf[i]); //字符型 tmphex:=tmphex+inttohex(rbuf[i],2); //16进制型 end; if trim(tmpstr)<>'' then viewstring:=viewstring+tmpstr; if tmphex='0A' then begin //Memo1.Lines.Add(viewstring); if Edit1.Text<>viewstring then Edit1.Text:=viewstring; viewstring:=''; end; end; //发送数据 procedure senddata; var i:integer; commflg:boolean; begin viewstring:=''; commflg:=true; for i:=1 to high(sbuf) do begin if not https://www.doczj.com/doc/a5263798.html,m1.writecommdata(@sbuf[i],1) then begin commflg:=false; break; end; //发送时字节间的延时 sleep(5); viewstring:=viewstring+inttohex(sbuf[i],2)+' '; if not commflg then messagedlg('发送失败!',mterror,[mbyes],0); end; viewstring:='发送'+viewstring; Form1.memo1.lines.add(viewstring); Form1.memo1.lines.add(''); end; procedure TForm1.sentcustom(); var str11:string ; i,j,k:integer; tmpstr:string; begin if Edit1.Text<>'' then begin str11:=Trimplace(Edit1.Text); //替换字符串中的所有空格 i:=round(length(str11)/2); //获得字符串长度,除2取整后加1 setlength(sbuf,i+1); //重新设定发送数组范围 for j:=1 to i do begin tmpstr:=copy(str11,j*2-1,2); if tmpstr='' then tmpstr:='00'; sbuf[j]:=byte(strtoint('$'+tmpstr)); //将变量转换为byte数组end; end else begin sbuf[1]:=byte($53); //帧头 sbuf[2]:=byte($54); sbuf[3]:=byte($2C); sbuf[4]:=byte($47); sbuf[5]:=byte($53); sbuf[6]:=byte($2C); sbuf[7]:=byte($2B); sbuf[8]:=byte($20); sbuf[9]:=byte($20); sbuf[10]:=byte($20); sbuf[11]:=byte($30); sbuf[12]:=byte($2E); sbuf[13]:=byte($30); sbuf[14]:=byte($30); sbuf[15]:=byte($20); sbuf[16]:=byte($6B); sbuf[17]:=byte($67); sbuf[18]:=byte($20); sbuf[19]:=byte($20); sbuf[20]:=byte($0D); sbuf[21]:=byte($0A); end; end;

使用串口UART0接收上位机发送的数据

/****************************************Copyright (c)************************************************** ** Guangzou ZLG-MCU Development Co.,LTD. ** graduate school ** https://www.doczj.com/doc/a5263798.html, ** **--------------File Info------------------------------------------------------------------------------- ** File name: main.c ** Last modified Date: 2004-09-16 ** Last Version: 1.0 ** Descriptions: The main() function example template ** **------------------------------------------------------------------------------------------------------ ** Created by: Chenmingji ** Created date: 2004-09-16 ** Version: 1.0 ** Descriptions: The original version ** **------------------------------------------------------------------------------------------------------ ** Modified by: ** Modified date: ** Version: ** Descriptions: ** ***************************************************************** ***************************************/ /**************************************************************** ************ * 文件名:main.C * 功能:使用串口UART0接收上位机发送的数据,并将数据原封不动地发送回上位机。 * 说明:通讯波特率115200,8位数据位,1位停止位,无奇偶校验。 ***************************************************************** ***********/

串口通信接收发送数据显示

1、接收数据 #include #define Data_d P0 #define Data_w P2 #define uint unsigned int #define uchar unsigned char unsigned char flag; unsigned char Duanma[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88};// 显示段码值01234567 code const unsigned char Weima[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//分别对应相应的数码管点亮,即位码 /*------------------------------------------------ 延时子程序 ------------------------------------------------*/ void delayms(unsigned int z) { unsigned int x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } ///*------------------------------------------------ // 定时器初始化子程序 //------------------------------------------------*/ //void Init_Timer0(void) //{ // TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响 // //TH0=0x00; //给定初值,这里使用定时器最大值从0开始计数一直到65535溢出 // //TL0=0x00; // EA=1; //总中断打开 // ET0=1; //定时器中断打开 // TR0=1; //定时器开关打开 //} /*----------------------------------------------- 串口初始化 ------------------------------------------------*/ void serial_init(void) { SCON = 0x50; /* SCON: 模式1, 8-bit UART, 使能接收REN=1,SM0=0,SM1=1 */ TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */

串口接收数据帧

#include "REG52.H" /* 注释一: * 请评估实际项目中一串数据的最大长度是多少,并且留点余量,然后调整const_rc_size 的大小。 * 本节程序把上一节的缓冲区数组大小10改成了20 */ #define const_rc_size 20 //接收串口中断数据的缓冲区数组大小 #define const_receive_time 5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小 void initial_myself(void); void initial_peripheral(void); void delay_long(unsigned int uiDelaylong); void T0_time(void); //定时中断函数 void usart_receive(void); //串口接收中断函数 void usart_service(void); //串口服务程序,在main函数里 void led_service(void); //Led灯的服务程序。 sbit led_dr=P3^5; //Led的驱动IO口 sbit beep_dr=P2^7; //蜂鸣器的驱动IO口 unsigned int uiSendCnt=0; //用来识别串口是否接收完一串数据的计时器 unsigned char ucSendLock=1; //串口服务程序的自锁变量,每次接收完一串数据只处理一次 unsigned int uiRcregTotal=0; //代表当前缓冲区已经接收了多少个数据 unsigned char ucRcregBuf[const_rc_size]; //接收串口中断数据的缓冲区数组 unsigned int uiRcMoveIndex=0; //用来解析数据协议的中间变量 /* 注释二: * 为串口计时器多增加一个原子锁,作为中断与主函数共享数据的保护,实际上是借鉴了"红金龙吸味"关于原子锁的建议. */ unsigned char ucSendCntLock=0; //串口计时器的原子锁 unsigned int uiV oiceCnt=0; //蜂鸣器鸣叫的持续时间计数器 unsigned char ucV oiceLock=0; //蜂鸣器鸣叫的原子锁 unsigned char ucRcType=0; //数据类型 unsigned int uiRcSize=0; //数据长度

串口调试助手

目前较为常用的串口有9针串口(DB9)和25针串口(DB25),通信距离较近时(<12m),可以用电缆线直接连接标准RS232端口(RS422,RS485较远),若距离较远,需附加调制解调器(MODEM)。最为简单且常用的是三线制接法,即地、接收数据和发送数据三脚相连,本文只涉及到最为基本的接法,且直接用RS232 相连。 1.DB9和DB25的常用信号脚说明 9针串口(DB9) 25针串口(DB25) 针号功能说明缩写针号功能说明缩写 1 数据载波检测 DCD 8 数据载波检测 DCD 2 接收数据 RXD 3 接收数据 RXD 3 发送数据 TXD 2 发送数据 TXD 4 数据终端准备 DTR 20 数据终端准备 DTR 5 信号地 GND 7 信号地 GND 6 数据设备准备好 DSR 6 数据准备好 DSR 7 请求发送 RTS 4 请求发送 RTS 8 清除发送 CTS 5 清除发送 CTS 9 振铃指示 DELL 22 振铃指示 DELL 2.RS232C串口通信接线方法(三线制) 首先,串口传输数据只要有接收数据针脚和发送针脚就能实现:同一个串口的接收脚和发送脚直接用线相连,两个串口相连或一个串口和多个串口相连 · 同一个串口的接收脚和发送脚直接用线相连对9针串口和25针串口,均是2与3直接相连; · 两个不同串口(不论是同一台计算机的两个串口或分别是不同计算机的串口) 上面表格是对微机标准串行口而言的,还有许多非标准设备,如接收GPS数据或电子罗盘数据,只要记住一个原则:接收数据针脚(或线)与发送数据针脚(或线)相连,彼此交叉,信号地对应相接,就能百战百胜。 3.串口调试中要注意的几点: 串口调试时,准备一个好用的调试工具,如串口调试助手、串口精灵等,有事半

单片机串口收发数据c程序

***************************************************************** 程序功能:计算机通过串口向单片机发送数据,单片机接受后会将接受到的数据重新发送给计算机。 本程序需用到串口调试助手软件,下载程序后,在发送区发送 数据后,会在上面的接受区显示单片机返回来的数据。 *****************************************************************/ #include #define uchar unsignedchar #define uint unsignedint uchar buf; void senddata(uchar dat) { SBUF =dat; while(!TI); TI = 0; } void main(void) { SCON=0x50; //设定串口工作方式 PCON=0x00; //波特率不倍增 TMOD=0x20; //定时器工作于位自动重载模式, 用于产生波特率 EA=1; ES = 1; //允许串口中断 TL1=0xfd; TH1=0xfd; //波特率 TR1=1; while(1); } /********************************************************* 串行中断服务函数 *********************************************************/ void serial() interrupt 4 { ES = 0; //关闭串行中断 RI = 0; //清除串行接受标志位 buf = SBUF; //从串口缓冲区取得数据 senddata(buf); ES = 1; //允许串口中断 }

ATmega16单片机实现串口收发数据

1、编译环境:CodeVisionAVR 2、功能:实现以5AH开头的6个字节数据帧的接收及发送 3、接收及发送方式:中断接收,查询发送 4、校验方式:所有字节相加模除256等于0则接收正确,否则不予接收程序如下所示:#include "mega16.h" #define uchar unsigned char #define uint unsigned int #define BAUD 9600 #define F_CLK 400000 0 #define MATCH_OK 1; #define MATCH_ERROR 0; #define UDRE 5 volatile uchar i=0; volatile uchar recc_flag=0; //命令字节接收标志(recieve command 简写成 recc) volatile uchar comm_flag=0; //命令帧接收完毕标志(command 简写成 comm) volatile uchar comm; //定义变量,用于传递UDR0中接收到的命令字 volatile uchar command[6]={0x00,0x00,0x00,0x00,0x00,0x00}; //定义长度为6的数组用于接收长度为6字节的命令帧,并将所有元素初始化为0x00 /*延时*/ void delay(uint t) { while(t--); } /*****/ /*******帧头校验******/ uchar check_comm(void) { uchar i; uint result=0; for(i=0;i<6;i++) { result+=command[i]; } if((result%256)==0) { return MATCH_OK; } else { return MATCH_ERROR; } } /*********************/ void uart_init(void) { UCSRA=0x00; UCSRB=0x98; //接收结束中断使能;数据接收使能;数据发送使能 UCSRC=0x06; //工作在异步模式;无校验;1位停止位;字符长度为8位 UBRRH=(F_CLK/BAUD/16-1)/256; UBRRL=(F_CLK/BAUD/16-1)%256; //系统时钟为4MHz,波特率为9600bps } void init_device(void) { #asm("cli") //关中断 PORTA=0x04; DDRA=0x04; PORTB=0xFE; DDRB=0xFF; PORTD=0xFF; DDRD=0x02; MCUCR=0x00; TIMSK=0x00; uart_init(); //串口初始化 #asm("sei") //重开中断 } interrupt[USART_RXC] void usart_rec(void) {

ZigBee串口收发数据

本文转载自:https://www.doczj.com/doc/a5263798.html,/space.php?uid=20788636&do=blog&id=1841411 串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。这里以SerialApp例程为例子。在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA 通道 void HalDriverInit (void) { ................................... #if (defined HAL_UART) && (HAL_UART == TRUE) HalUARTInit(); #endif .................................... } 从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UART TRUE 在hal_board_cfg.h文件中。 #ifndef HAL_UART #if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2) #define HAL_UART TRUE #else #define HAL_UART FALSE #endif #endif 然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。 在CC2430的数据手册中有这样一段话。 Data reception on the UART is initiatedwhen a 1 is written to the UxCSR.RE bitThe UART will then search for a valid start bit on the RXDx input pin and set theUxCSR.ACTIVE bit high. When a validstart bit has been detected the received byte is shifted into the receive register .The UxCSR.RX_BYTE bit is set and a receive interrupt is generated when the operation has completed. The received data byte is available through the UxBUF register. When UxBUF is read, UxCSR.RX_BYTE is cleared by hardware. 当有数据接收时,UxCSR.RE位将被置1,然后,UART将在RXDx的输入引脚上查找一个有效的开始位,当找到这个开始位时,将设置UxCSR.ACTIVE位为高电平。当一个有效的开始位被查找到,收到的字节将被移动到接收寄存器中。然后,UxCSR.RX_BYTE位设为1.并且,当这个接收操作完成后接收中断会被产生。接收到的数据可以通过操作UxBUF 寄存器,当UxBUF寄存器的数据被读出后,UxCSR.RX_BYTE位被硬件清除。串口发生中断首先调用中断的处理函数,这个是接收的中断函数。 #if HAL_UART_0_ENABLE HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR ) { cfg0->rxBuf[cfg0->rxHead] = U0DBUF; if ( cfg0->rxHead == cfg0->rxMax ) { cfg0->rxHead = 0;

将串口接收到的数据保存到EXCEL文档资料讲解

将串口接收到的数据保存到E X C E L文档

《一》 用VB DDE通讯! 《二》 1.启动excel从:工具-->宏-->Visual Basic 编辑器,打开excel VBA. 2.在ThisWorkbook上右单击鼠标选择插入--用户窗体 3.单击一下插入的窗体,单击菜单上的--工具--附加控件--选择Microsoft Communications Control, version 6.0 4.在窗体上添加mscomm1,添加commandButton1 5.单串口机子,短接rs232的2脚和3脚,双串口机子用232线连接两个串口,注意2、3线交叉,我这里以单串口短接举例。 6.复制以下代码到你的窗体里: VB code 'VBA代码 Private Sub CommandButton1_Click() MSComm1.Output = "BEG1END" End Sub Private Sub MSComm1_OnComm() Dim t1 As Long, com_String As String Static i As Integer t1 = Timer Select Case https://www.doczj.com/doc/a5263798.html,mEvent Case comEvReceive '收到 RThreshold定义的字符数1字节 MSComm1.RThreshold = 0 Do DoEvents Loop While Timer - t1 < 0.1 '延时时间自己调整 com_String = MSComm1.Input MSComm1.RThreshold = 1 i = i + 1: If i > 255 Then i = 1 Application.Cells(3, i).Value = com_String End Select 'ActiveWorkbook.SaveAs Filename:="C:\d1.xls" End Sub

电脑串口接收数据程序

本程序用于电脑串口接收数据 SerialPort.h #ifndef SERIALPORT_H_ #define SERIALPORT_H_ #include #include "TChar.h" #include #include #include #include #include #include #include #include using namespace std; /** 串口通信类 * * 本类实现了监听发到指定串口的数据 */

class CSerialPort { public: CSerialPort(void); ~CSerialPort(void); public: /** 初始化串口函数 * * @param: UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9 * @param: UINT baud 波特率,默认为9600 * @param: char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验 * @param: UINT databits 数据位的个数,默认值为8个数据位 * @param: UINT stopsbits 停止位使用格式,默认值为1 * @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件 * @return: bool 初始化是否成功 * @note: 在使用其他本类提供的函数前,请先调用本函数进行串口的初始化 * /n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB 参数,可使用重载函数

串口通信测试方法

串口通信测试方法 1关于串口通信的一些知识: RS-232C 是目前最常用的串行接口标准,用来实现计算机和计算机之间、计算 机和外设之间的数据通信。 在PC 机系统中都装有异步通信适配器,利用它可以实现异步串行通信。而且 MCS-51单片机本身具有一个全双工的串行接口,因此只要配以电平转换的驱动 电路、隔离电路就可以组成一个简单可行的通信接口。 由于MCS-51单片机的输入和输出电平为 TTL 电平,而PC 机配置的是RS-232C 标准串行接口,二者电气规范不一致,因此要完成 PC 机与单片机的数据通信, 必须进行电平转换。 注明:3)RS-232C 上传送的数字量采用负逻辑,且与地对称 逻辑1: -3?-15V 逻辑0: +3?+15V 所以与单片机连接时常常需要加入电平转换芯片: 2实现串口通信的三个步骤: (1)硬件连接 51单片机有一个全双工的串行通讯口, 所以单片机和计算机之间可以方便 地进行串口通讯。进行串行通讯时要满足一定的条件,比如计算机的串口是 RS232电平的,而单片机的串口是 TTL 电平的,两者之间必须有一个电平转换电 路,我们采用了专用芯片 MAX232S 行转换。我们采用了三线制连接串口,也就 是说和计算机的9针串口只连接其中的3根线:第5脚的GND 第2脚的RXD 第3脚的TXD 电路如下图所示,MAX232的第10脚和单片机的11脚连接,第9 脚和单片机的10脚连接,第15脚和单片机的20脚连接。 使用MAX23舉口通信电路图(9孔串口接头) (2)串行通信程序设计 ① 通信协议的使用 通信协议是通信设备在通信前的约定。单片机、计算机有了协议这种约定, 通信双方才能明白对方的意图,以进行下一步动作。假定我们需要在 PC 机与单 f : MH v 广 CAP ? C A? ? CAP 2* CAP 1* CAP 2- CAP h RT^OUTL Tim R-OUT1 *"2 R-0UT2 €601 IOvF C 机H 印—— MAX232 16 VCC 10 PC 血

串口通信数据传输方式描述

串口通信数据传输方式描述 通信方式 说明单工,半双工,全双工通信的意义 根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。信息只能单向传送为单工(打印机工作方式) ; 信息能双向传送但不能同时双向传送称为半双工(对讲机工作方式); 通信方式单工(Simplex Communication)模式的数据传输是单向的。通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输,使用一根传输线。单工通信是指通信线路上的数据按单一方向传送. 单工模式一般用在只向一个方向传输数据的场合。例如计算机与打印机之间的通信是单工模式,因为只有计算机向打印机传输数据,而没有相反方向的数据传输。还有在某些通信信道中,如单工无线发送等。 半双工通信使用同一根传输线,既可以发送数据又可以接收数据,但不能同时进行发送和接收。数据传输允许数据在两个方向上传输,但是,在任何时刻只能由其中的一方发送数据,另一方接收数据。因此半双工模式既可以使用一条数据线,也可以使用两条数据线。它实际上是一种切换方向的单工通信,就和对讲机(步话机) 一样。半双工通信中每端需有一个收发切换电子开关,通过切换来决定数据向哪个方向传输。因为有切换,所以会产生时间延迟。信息传输效率低些 半双工(Half Duplex),所谓半双工就是指一个时间段内只有一个动作发生,举个简单例子,一条窄窄的马路,同时只能有一辆车通过,当目前有两量车对开,这种情况下就只能一辆先过,等到头儿后另一辆再开,这个例子就形象的说明了半双工的原理。早期的对讲机、以及早期集线器 等设备都是基于半双工的产品。随着技术的不断进步,半双工会逐渐退出历史舞台. 全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,就和电话一样。在全双工模式中,每一端都有发送器和接收器,有两条传输线,可在交互式应用和远程监控系统中使用,信息传输效率高。 全双工(Full Duplex)是指在发送数据的同时也能够接收数据,两者同步进行,这好像我们平时打电话一样,说话的同时也能够听到对方的声音。目前的网卡一般都支持全双工。

将串口接收到的数据保存到ecel文档

《一》 用VB DDE通讯! 《二》 1.启动excel从:工具-->宏-->Visual Basic 编辑器,打开excel VBA. 2.在ThisWorkbook上右单击鼠标选择插入--用户窗体 3.单击一下插入的窗体,单击菜单上的--工具--附加控件--选择Microsoft Communications Control, version 4.在窗体上添加mscomm1,添加commandButton1 5.单串口机子,短接rs232的2脚和3脚,双串口机子用232线连接两个串口,注意2、3线交叉,我这里以单串口短接举例。 6.复制以下代码到你的窗体里: VB code 'VBA代码 Private Sub CommandButton1_Click() = "BEG1END" End Sub Private Sub MSComm1_OnComm() Dim t1 As Long, com_String As String

Static i As Integer t1 = Timer Select Case Case comEvReceive '收到 RThreshold定义的字符数1字节 = 0 Do DoEvents Loop While Timer - t1 < '延时时间自己调整 com_String = = 1 i = i + 1: If i > 255 Then i = 1 (3, i).Value = com_String End Select ' Filename:="C:\" End Sub Private Sub iniMscomm() 'On Error Resume Next '=====-----初始化通信串口-----===== = 1 '使用 COM1 = "9600,N,8,1" '9600 波特,无奇偶校验,8 位数据,一个停止位 = True '打开端口 = 1 '缓冲区有1个字节就产生OnComm事件

串口通信实验程序(数据接收)

串口通信实验程序(数据接收) 试验效果如下:#include#define duan P0//段选#define wei P2//位选unsigned char code wei1[8] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位选控制查表的 方法控制unsigned char code duan1[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0 x71};//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F 的显示码 unsigned char ge,shi,bai,temp;void DigDisplay(); //动态显示函数void init(); //初始化函数void main(void){init();while(1){DigDisplay();} } void DigDisplay(){unsigned char i,j;bai=temp/100;shi=temp%100/10;ge=temp%10; wei = wei1[i];//发送位选duan = duan1[bai]; //发送段码j = 10;//扫描间隔时间设 定while(j--);duan = 0x00; //消隐i++; wei = wei1[i];//发送位选duan = duan1[shi]; //发送段码j = 10;//扫描间隔时间设 定while(j--);duan = 0x00; //消隐i++; wei = wei1[i];//发送位选duan = duan1[ge]; //发送段码j = 10;//扫描间隔时间设 定while(j--);duan = 0x00; //消隐i=0;}void init() {TMOD=0x20;TH1=0xfd;TL1=0xfd;EA=1;TR1=1;SM1=1;REN=1;ES=1;}void usb() interrupt 4{RI=0;temp=SBUF;} tips:感谢大家的阅读,本文由我司收集整编。仅供参阅!

相关主题
文本预览
相关文档 最新文档