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

11 篇文章 0 订阅

控制器与外界通讯,一般都会使用一些约定的通讯协议,或者使用类似于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();
	else
		m_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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值