在SylixOS中CAN报文的传输框图如图 11所示。
图 11 SylixOS CAN报文传输框图
(注:此文档承接之前的文档编写,之前文档中详细介绍过的报文,传输结构体在此文档中不做详细介绍。)
在SylixOS中CAN报文的传输不是底层和上层应用的直接传输。而是在底层和应用层中间加了一层系统缓存队列。所有收发的CAN报文都要先经过一个系统缓存机制传输到真正调用到它的地方。
SylixOS中CAN报文是以消息队列的方式进行缓存的,程序清单 21是向消息队列中写入一帧CAN报文的具体实现。
程序清单 21从缓存中读取一帧CAN报文
/*********************************************************************************************************
** 函数名称: __canITx
** 功能描述: 从发送缓冲区中读出一个数据
** 输 入 :
** pcanDev CAN 设备
** pcanframe 指向待读出的数据
** 输 出 : ERROR_NONE or PX_ERROR
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT __canITx (__CAN_DEV *pcanDev, PCAN_FRAME pcanframe)
{
INTREG iregInterLevel;
INT iTemp = 0;
if (!pcanDev || !pcanframe) {
return (PX_ERROR);
}
iTemp = __canReadQueue(pcanDev,
pcanDev->CAN_pcanqSendQueue,
pcanframe, 1); /* 从发送队列中读取一帧数据 */
LW_SPIN_LOCK_QUICK(&pcanDev->CAN_slLock, &iregInterLevel);
if (iTemp <= 0) {
pcanDev->CAN_canstatWriteState.CANSTAT_bBufEmpty = LW_TRUE; /* 发送队列空 */
}
LW_SPIN_UNLOCK_QUICK(&pcanDev->CAN_slLock, iregInterLevel);
API_SemaphoreBPost(pcanDev->CAN_ulSendSemB); /* 释放信号量 */
SEL_WAKE_UP_ALL(&pcanDev->CAN_selwulList, SELWRITE); /* 释放所有等待写的线程 */
return ((iTemp) ? (ERROR_NONE) : (PX_ERROR));
}
上层应用向底层传输一帧CAN报文的时候也是通过系统缓存,向系统缓存中写入一帧CAN报文的具体实现如程序清单 22所示。
程序清单 22向缓存中写入一帧CAN报文
/*********************************************************************************************************
** 函数名称: __canIRx
** 功能描述: 向接收缓冲区中写入一个数据
** 输 入 :
** pcanDev CAN 设备
** pcanframe 指向待写入的数据
** 输 出 : ERROR_NONE or PX_ERROR
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT __canIRx (__CAN_DEV *pcanDev, PCAN_FRAME pcanframe)
{
INT iTemp = 0;
if (!pcanDev || !pcanframe) {
return (PX_ERROR);
}
iTemp = __canWriteQueue(pcanDev,
pcanDev->CAN_pcanqRecvQueue,
pcanframe, 1); /* 往接收队列中写入一帧数据 */
API_SemaphoreBPost(pcanDev->CAN_ulRcvSemB); /* 释放信号量 */
SEL_WAKE_UP_ALL(&pcanDev->CAN_selwulList, SELREAD); /* select() 激活 */
return ((iTemp) ? (ERROR_NONE) : (PX_ERROR));
}
第一步:如程序清单 31所示,在应用层创建一个线程,打开一个CAN设备。
程序清单 31打开CAN设备
iFd = open(devname, O_RDWR, 0666);
if (iFd < 0) {
printf("failed to open %s!\n", devname);
return (LW_NULL);
}
第二步:如程序清单 32所示,填充一个CAN报文结构体。
程序清单 32填充CAN报文
CAN_FRAME canframe;
canframe.CAN_bExtId = LW_FALSE;
canframe.CAN_bRtr = LW_FALSE;
canframe.CAN_ucLen = CAN_MAX_DATA;
lib_memcpy((CHAR *)canframe.CAN_ucData, "01234567", CAN_MAX_DATA);
canframe.CAN_uiId = 0;
第三步:如程序清单 33所示,调用write函数向系统TX缓存队列中写入一帧CAN报文,再调用ioctl函数实现底层传输。
程序清单 33填充发送缓存
stLen = write(iFd, &canframe, sizeof(CAN_FRAME));
ioctl(iFd, CAN_DEV_STARTUP, 0);
case CAN_DEV_STARTUP:
__flexcanStartup(pCanchan);
break;
第四步:如程序清单 34所示,最终调用到底层传输函数。从系统队列中读取一帧CAN报文后对设备寄存器进行相关操作将消息传输到总线上。
程序清单 34底层starup函数
/*********************************************************************************************************
** 函数名称: __flexcanStartup
** 功能描述: 启动数据发送
** 输 入 : pCanchan 通道对象
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static INT __flexcanStartup (CAN_CHAN *pCanchan)
{
FLEXCAN_CHAN *pChannel = container_of(pCanchan, FLEXCAN_CHAN, CANCH_canchan);
CAN_FRAME canFrame;
INT iCount;
if (!pChannel->CANCH_pcbGetTx) {
return (PX_ERROR);
}
while (pChannel->CANCH_pcbGetTx(pChannel->CANCH_pvGetTxArg, /* 从发送缓冲区中读取数据发送 */
&canFrame) == ERROR_NONE) {
__flexcanSend(pChannel, &canFrame);
}
return (ERROR_NONE);
}
注:如果想要发送多帧CAN报文,在写入操作结束后需要加等待,以确保所有的CAN报文都成功写入系统缓存队列中。
第一步:底层如果接收到CAN报文以后,会触发一次中断,在中断服务函数中所做的事就是判断状态标志位置,如果是接收中断,就把接收到的CAN报文通过回调函数写入,系统缓存队列中,具体实现如程序清单 35所示。
程序清单 35 CAN底层中断服务函数
/*********************************************************************************************************
** 函数名称: __flexcanIrq
** 功能描述: can 中断服务程序
** 输 入 : pChannel 通道对象
** ulVector 中断向量号
** 输 出 : ERROR CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static irqreturn_t __flexcanIrq (PVOID pvArg, ULONG ulVector)
{
FLEXCAN_CHAN *pChannel;
UINT32 uiIflag1, uiEsr, uiValue;
CAN_FRAME canframe;
can_debug("[CAN]irq\r\n");
pChannel = (FLEXCAN_CHAN *)pvArg;
uiIflag1 = CAN_READ(FLEXCAN_IFLAG1);
uiEsr = CAN_READ(FLEXCAN_ESR1);
uiValue = CAN_READ(FLEXCAN_CTRL1);
uiValue &= ~(FLEXCAN_CTRL1_ERR_ALL);
CAN_WRITE(FLEXCAN_CTRL1, uiValue);
CAN_WRITE(FLEXCAN_IMASK1, 0);
if (uiEsr & FLEXCAN_ESR1_ERR_ALL) {
can_debug("There is something wrong!\n");
CAN_READ(FLEXCAN_ESR1);
CAN_WRITE(FLEXCAN_ESR1, FLEXCAN_ESR1_ERR_ALL);
}
if (uiEsr & FLEXCAN_ESR1_RX) {
if (uiIflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
CAN_WRITE(FLEXCAN_IMASK1, FLEXCAN_IFLAG_DEFAULT & ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE);
memset(&canframe, 0, sizeof(CAN_FRAME));
__flexcanRecv(pChannel, &canframe);
if (pChannel->CANCH_pcbPutRcv(pChannel->CANCH_pvPutRcvArg, &canframe) != ERROR_NONE) {
pChannel->CANCH_pcbSetBusState(pChannel->CANCH_pvSetBusStateArg,
CAN_DEV_BUS_RXBUFF_OVERRUN);
}
}
}
if (uiIflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
can_debug("FIFO overflow\n");
CAN_WRITE(FLEXCAN_IFLAG1, FLEXCAN_IFLAG_RX_FIFO_OVERFLOW);
}
CAN_WRITE(FLEXCAN_IMASK1, FLEXCAN_IFLAG_DEFAULT);
return (LW_IRQ_HANDLED);
}
第二步:在应用程序中创建一个线程,在线程中所做的事情就是不间断得读取系统缓存的消息队列。如果缓存不为空,就读取里面的CAN报文,并打印,具体操作如程序清单 36所示。
程序清单 36 CAN应用层读取缓存
while (1) {
stLen = read(iFd, &canframe, sizeof(STR_CANMSG_T));
stFrameNum = stLen / sizeof(STR_CANMSG_T);
if (stFrameNum != 1) {
printf("failed to recv can frame, abort recving!\n");
break;
} else {
sprintf(cFramInfo, "id=%d, len=%d, data=%02x %02x %02x %02x %02x %02x %02x %02x\n",
canframe.Id, (INT)canframe.DLC,
canframe.Data[0],
canframe.Data[1],
canframe.Data[2],
canframe.Data[3],
canframe.Data[4],
canframe.Data[5],
canframe.Data[6],
canframe.Data[7]);
printf(cFramInfo);
}
}
CAN报文传输流程,到此结束。
内部交流文档,若发现相关错误或者建议,请及时联系文档创建者进行修订和更新。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。