DSP 2812: 使用C++实现的SCI主动站程序框架

来源:互联网 发布:origin8.5软件下载 编辑:程序博客网 时间:2024/05/17 01:05

控制器与外界通讯,一般都会使用一些约定的通讯协议,或者使用类似于linux控制台的字符终端交互。

大体上有两种通讯模式。一种是接收上位机之类的主机的命令,并执行和输出命令的执行结果或状态。在这种被动模式,开发板称为从动站;另外一种是定时发送命令给其他控制板或者仪表,读取或者控制其他设备。在这种主动模式,开发板被称为主动站。


我们创建了这两张模式下的程序框架。

这里介绍的是做为主动站的程序框架。


应用程序的编写

首先展示一下应用程序中是如何使用这个驱动的:


第一步初始化串口设备

scia().setBps(9600,board.clock());

我们这里使用的SCIA,设置通讯速率是9600.其他参数使用默认值。

第二步定义Scia主站类对象,并设置收发缓冲区:

unsigned char sciRxBuf[256];unsigned char sciTxBuf[256];NDm::NApp::NF281x::CSciAMaster& sciaMaster = NDm::NApp::NF281x::CSciAMaster::ins();sciaMaster.setRxBuf(sciRxBuf,256);sciaMaster.setTxBuf(sciTxBuf,256);sciaMaster.reset();

第三步在应用程序中实现报文处理的业务程序。

基本就是这样的循环:发送数据,然后接收数据,处理数据。

我的例子程序使用调试助手,接收DSP发来的start后,就发数据,然后DSP回复发送的数据,再发数据,再回复这样的循环。

unsigned char buf[10] = "start\n";int num = 6;sciaMaster.send(buf,num);while( true ){num = sciaMaster.recv(buf,10);if( num>0 ){sciaMaster.send(buf,num);}}

驱动程序的设计

驱动程序时设计一个基类,然后为各串口实现子类。在子类中设定中断向量和相应的处理串口类。


基类的设计

namespace NF281x {/** * SCI主模块驱动 * 由作为主动站的程序调用本类接口。 */class CSciMaster {public:typedef TRingBuf<unsigned char,unsigned int,unsigned int> CRingBuf;typedef CRingBuf::CRingPos CRingPos;public:CSciMaster( NDm::NHw::NF281x::CSci& sci  );

private:CRingBuf m_rx;CRingPos m_rxPos;CRingBuf m_tx;CRingPos m_txPos;NDm::NHw::NF281x::CSci& m_sci;NDm::NApp::NF281x::CPieCtl& m_pie;};} /* namespace NF281x */

这个类使用了两个循环缓冲区,用于缓存接收到的数据和要发送的数据。

定义了简单使用的接口函数

void reset();bool isError()const;inline NDm::NHw::NF281x::CSci& sci(){return m_sci;}inline const NDm::NHw::NF281x::CSci& sci()const{return m_sci;}inline void setTxBuf( unsigned char* buf,const unsigned int& size ){m_tx.setBuf(buf,size);}inline void setRxBuf( unsigned char* buf,const unsigned int& size ){m_rx.setBuf(buf,size);}inline bool ifTxEmpty()const{return m_tx.getPos()==m_txPos;}inline bool ifRxEmpty()const{return m_rx.getPos()==m_rxPos;}int send( const unsigned char* buf,const int& size );int recv( unsigned char* buf,const int& size );

应用程序主要使用的函数就是send和recv函数。

因为驱动程序自带缓冲区,所以应用程序调用收发函数后,可以立即返回去处理其他业务,而不用等到串口数据发送完成。

/** * 中断处理函数 * 这些函数应该在中断函数中被调用执行 * 用户程序应该执行PIE级别的ACK操作 */void irsRx();void irsTx();inline NDm::NHw::NF281x::CPie& pie(){return m_pie;}

真正的在SCI设备上收发数据在irsRx()和irsTx()中实现。这两个函数由中断调用。

这块可以详细看看收发函数是如何实现的。

void CSciMaster::irsRx(){unsigned char buf[16];unsigned int size;if( m_sci.isInt_rxFf() ){// 接收中断,将接收到的数据保存到缓存中size = m_sci.rx(buf,16);if( size>0 )m_rx.push(buf,size);m_sci.clrInt_rxFf();}else{reset();}}

可以看出来,接收中断中,只是将RXFIFO中的数据转移到接收的循环缓冲区中,并清除接收中断标志。

而对于发送的过程可能要复杂一些。

因为发送数据是由中断之外触发的。

int CSciMaster::send( const unsigned char* buf,const int& size ){int rt = m_tx.getSize() - m_tx.getLen(m_txPos);if( rt>size )rt = size;if( rt<=0 )return 0;m_tx.push(buf,rt);m_sci.disInt_txFf();// 禁用发送中断startTx();return rt;}

发送函数将要发送的数据存入发送缓冲区中。在这个过程中,如果有发送中断到来,是不会影响send函数的正确性的,因为发送缓冲区,使用了两个pos对象,一个是写,一个是读。

但是真要发送数据时,是必须要禁止发送中断的。然后将缓冲区中的数据写到TXFIFO中,这个过程由startTx()函数实现。

为什么要有startTx()函数,因为这个过程在发送中断中也使用了。

void CSciMaster::irsTx(){startTx();// 如果队列为空,则停止中断if( ifTxEmpty() )m_sci.disInt_txFf();}

startTx()函数做了什么?

void CSciMaster::startTx(){unsigned char buf[16];unsigned int size;size = m_tx.getData(buf,16,m_txPos);if( size>0 ){size = m_sci.tx(buf,size);m_txPos += size;}// 清除中断标志m_sci.clrInt_txFf();// 如果队列为空,则停止中断if( ifTxEmpty() )m_sci.disInt_txFf();elsem_sci.enInt_txFf();}

这样的话,我将recv()函数的实现也展示出来:

int CSciMaster::recv( unsigned char* buf,const int& size ){int rt = m_rx.getLen(m_rxPos);if( rt>size )rt = size;if( rt<=0 )return 0;m_rx.getDataAndMovePos(buf,rt,m_rxPos);return rt;}

这样的话,这个驱动就算是完整了。

其实这样的程序要考虑清楚各种并非事件之间的关系,成员变量是否会受影响,是否会影响程序的正确性,也不是一件容易的事情。

还好,这块的代码经过测试和使用,是没问题的。


其实Ti的实时操作系统SYSBIOS中的DEV模型也是非常好的框架,我在28335平台上就是实现了一个DEV的驱动。

子类的实现

以Scia为例

namespace NF281x {class CSciAMaster:public CSciMaster{CSciAMaster();public:static CSciAMaster& ins();};

在其构造函数中需要对其进行特殊化处理

CSciAMaster::CSciAMaster():CSciMaster(NDm::NApp::NF281x::CSciaCtl::ins()){CCpu::dint();pie().setIrs_rxAInt(irsDm281xSciAMasterRx);pie().setIrs_txAInt(irsDm281xSciAMasterTx);CCpu::eint();CCpu::ertm();}CSciAMaster& CSciAMaster::ins(){static CSciAMaster i;return i;}

可以看出,还需要两个中断函数进行封装

extern "C" interrupt void irsDm281xSciAMasterRx(){CSciAMaster& sci = CSciAMaster::ins();sci.irsRx();sci.pie().ack_rxAInt();}extern "C" interrupt void irsDm281xSciAMasterTx(){CSciAMaster& sci = CSciAMaster::ins();sci.irsTx();sci.pie().ack_txAInt();}

中断函数的处理也比较简单,只需要将执行权传递给相应的中断处理成员函数即可。

0 0
原创粉丝点击