HC32 CAN通信
HC CAN简介和ST对比
-
HC CAN
手册里面明确写出有两种不同类型的缓冲器,PTB 优先于STB。这点和ST的不一样。
HC的结构图
-
ST
ST CAN结构借用一下野火的图展示一下
STM32 的有两组 CAN 控制器,其中 CAN1 是主设备,框图中的“存储访问控制器”是由 CAN1 控制的, CAN2 无法直接访问存储区域,所以使用 CAN2 的时候必须使能CAN1 外设的时钟。
波特率设定
CAN 通信使用时钟 can_clk 的时钟源为外部高速振荡器, 使用 CAN 模块之前,需要设定 CAN 通信时钟。下图给出 CAN 位时间定义图,虚线上部分为 CAN 协议规定的位时间, 虚线下部分为本 CAN 控制器 CAN-CTRL 定义的位时间。 其中 segment1 和 segment2 可以通过寄存器 SBT 设定。 SBT 寄存器只能在 CAN_CFG_STAT.RESET=1 即 CAN 软件复位时设定。
注意:HC的CAN的时间片段并不是按照标准的CAN协议制定的。只有seg1和Seg2 两个参数,这个和ST的有不一样。在设置HCseg1 和seg2 的时候还需要满足手册上时间价格的要求,具体如下:
筛选器
CAN_CTRL 提供 16 组 32 位筛选器用于过滤接收到的数据从而降低 CPU 负荷, 筛选器可以支持标准格式 11 位 ID 或者扩展格式 29 位 ID。每组筛选器有一个 32 位 ID CODE 寄存器和一个 32 位 ID MASK 寄存器, ID CODE 寄存器用于比较接收到 CAN ID, 而 ID MASK 寄存器用于选择比较的 CAN ID 位。
对应的 ID MASK 位为 1 时,不比较该位的 ID CODE
。
注意: HC的 CODE模式和ST32的基本上一直表示 只有CAN ID和code寄存器里面的一样才会被接收。MASK模式也和ST32的掩码模式类似,进行过滤筛选,值得注意的是ST32的掩码模式相应的位置被设置为1后,进行掩码为的对比,只要掩码(关键字)相同,就符合要求,报文就会被保存到接收 FIFO。 但是HC的掩码模式是相应的掩码位设置为1后,是不对比CAN ID对应的改位的,反而设置为0后才需要和ID进行匹配。即 ST掩码模式设置1的是需要比对的ID位,而HC设置为1的是需要忽略的位。
需要注意的坑
- CAN 的一些设置完全可以参照dll库中提供的配置方式进行为微调。值得注意的是CAN的中断模式是全部打开的,在设计的时候需要先将所有的全部失能,然后再根据自己的需求进行配置。
CAN_IntCmd(TYPE_CANX, CAN_INT_ALL, Disable);
CAN_IntCmd(TYPE_CANX, CAN_INT_RX | CAN_INT_ERR_INT
|XXXXX| XXXXX | XXXXXX,
Enable);
- stc_can_tx_t结构体
typedef struct
{
uint32_t u32ID; /*!< 11 bits standard ID or 29 bits extended ID, depending on IDE. */
union
{
uint32_t u32Ctrl;
struct
{
uint32_t DLC: 4; /*!< Data length code. Length of the data segment of data frame. \
It should be zero while the frame is remote frame. \
This parameter can be a value of @ref CAN_DLC */
uint32_t BRS: 1; /*!< Bit rate switch. */
uint32_t FDF: 1; /*!< CAN FD frame. */
uint32_t RTR: 1; /*!< Remote transmission request bit.
It is used to distinguish between data frames and remote frames. */
uint32_t IDE: 1; /*!< Identifier extension flag.
It is used to distinguish between standard format and extended format.
This parameter can be a 1 or 0. */
uint32_t RSVD: 24; /*!< Reserved bits. */
};
};
uint8_t *pu8Data; /*!< Pointer to data filed of data frame. */
} stc_can_tx_t
需要注意这里面是一个联合体的形式,在设置DLC、BRS等后一定不要轻易的设置u32Ctrl,不然参数设置就会出现问题。
- stc_can_rx_t 结构体
typedef struct
{
uint32_t u32ID; /*!< 11 bits standard ID or 29 bits extended ID, depending on IDE. */
union
{
uint32_t u32Ctrl;
struct
{
uint32_t DLC: 4; /*!< Data length code. Length of the data segment of data frame. \
It should be zero while the frame is remote frame. \
This parameter can be a value of @ref CAN_DLC */
uint32_t BRS: 1; /*!< Bit rate switch. */
uint32_t FDF: 1; /*!< CAN FD frame. */
uint32_t RTR: 1; /*!< Remote transmission request bit.
It is used to distinguish between data frames and remote frames. */
uint32_t IDE: 1; /*!< Identifier extension flag.
It is used to distinguish between standard format and extended format.
This parameter can be 1 or 0. */
uint32_t RSVD: 4; /*!< Reserved bits. */
uint32_t TX: 1; /*!< This bit is set to 1 when receiving self-transmitted data in loopback mode. */
uint32_t ERRT: 3; /*!< Error type. */
uint32_t CYCLE_TIME: 16; /*!< Cycle time of time-triggered communication(TTC). */
};
};
uint8_t *pu8Data; /*!< Pointer to data filed of data frame. */
} stc_can_rx_t;
这个需要注意 uint8_t *pu8Data 这个只是一个指针,在使用之前必须赋值接收数据的缓存区,St32的在结构体里面是一个数组,HC这样设计可能是为了兼容CANFD协议。
-
CAN_TransData(M4_CAN_TypeDef *CANx, const stc_can_tx_t *pstcTx,
uint8_t u8TxBufType, uint8_t u8STBTxCtrl, uint32_t u32Timeout)
这个函数中有一个功能即 timeout,使用该功能需要将发送完成中断打开并在中断函数中及时清零,不然在发送的是时候回一直卡在其中,数据发送异常。 -
发送函数
发送函数的时候自己遇到了一个奇怪的问题,不知道具体原因,但经过更改后基本上能用
使用PTB发送,使用这种发送方式 伪代码如下
//发送单包数据
bool CAN_Send_Data(eBsp_Can can,
uint32_t StdId,
uint32_t ExtId,
uint8_t Ide,
uint8_t Rtr,
uint8_t *Data,
int len
)
{
uint16_t tranLen = 0;
pCANx_TypeDef CANx = NULL;
stc_can_tx_t stcTx;
int countUs = 0;
int i;
int errrr = 0;
//获取CAN 实例
CANx = CANInstance(can);
if(NULL == CANx || NULL == Data)
{
return 0;
}
if(len >= 8)
{
len = 8;
}
stcTx.u32Ctrl = 0x0U;
stcTx.DLC =GetDlc(can, len);
stcTx.u32ID = ExtId;
stcTx.IDE = Ide; // 0- 标准ID 1 - 扩展ID
stcTx.pu8Data = Data;
///
///while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U))) != Ok)
while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_PTB,CAN_STB_TRANS_ALL , 0U))) != Ok)
{
delayus(10);
countUs += 10;
if(countUs >= 100000)
{
log("CAN_Send_Data Send Error[No Send][%d]\r\n", CAN_GetErrType(CANx));
}
log(" [%d] %d \r\n", CAN_GetErrType(CANx),errrr);
}
return FALSE;
}
//发送多包数据
void CAN_SendBuff(uint8_t *buf, int Len)
{
#define LEN 8
int remains = 0;
int pkt = 0;
int i = 0;
uint8_t dstAddr;
uint8_t srcAddr;
int index = 0;
int j =0;
//addr为id
pkt = Len / 8;
remains = Len % 8;
for(i=0; i< pkt; i++)
{
CAN_Send_Data(CAN1,
0,
ID, //CAN 地址
1,
0,
(buf + index), //数据地址
LEN);
index += LEN;
}
if(remains > 0)
{
CAN_Send_Data(CAN1,
0,
ID, //CAN 地址
1,
0,
(buf + index), //数据地址
remains);
}
#undef LEN
}
问题是使用CAN_TransData(CANx, &stcTx, CAN_BUF_PTB,CAN_STB_TRANS_ALL , 0U) 发送的时候所有的数据都能发送完成,(while中出现重发,错误码中显示正在发送,即可以理解为上一包为发送完成)在CAN总线上使用设备抓包,数据完全能抓的到。
但是使用CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U),压力测试过程中, 发送端通过测试时显示buff的数据发送完成,在CAN总线上使用设备抓包,数据不完整,在while中errrr 显示正在发送 CAN_GetErrType()显示没有错误。由此推测,can在将数据放到FIFO中的3个邮箱时,邮箱还是满的。并且数据放到邮箱的数据可能存在数据丢失的可能(在压力测试的情况下,1ms发送一包数据)。
猜想验证
bool CAN_Send_Data(eBsp_Can can,
uint32_t StdId,
uint32_t ExtId,
uint8_t Ide,
uint8_t Rtr,
uint8_t *Data,
int len
)
{
//同上面函数
………………
while(Set == CAN_GetStatus(CANx,CAN_FLAG_TB_FULL))
{
i++;
delayus(CAN_LOOP_US);
if( i >= 5000)
{
CAN_LOG("TB full %d\r\n",i);
}
}
// //猜测发完后可能还要进行一些动作要延时
// if( i > 0)
// {
// delay_US(500);
// }
while(((errrr =CAN_TransData(CANx, &stcTx, CAN_BUF_STB,CAN_STB_TRANS_ALL , 0U))) != Ok)
{
delayus(10);
countUs += 10;
if(countUs >= 100000)
{
log("CAN_Send_Data Send Error[No Send][%d]\r\n", CAN_GetErrType(CANx));
}
log(" [%d] %d \r\n", CAN_GetErrType(CANx),errrr);
}
}
更改为判断FIFO 不满的时候在填数据到fifo中,在压力测试的时候同样出现 发送端数据发送完成,总线上数据不完整,接收端 数据异常。
还需要加上一个延时才能正常发送。具体原因未知 试验的时候CAN的速率比较低,可能是改为题导致,后面验证一下高速率1M的时候看是什么情况。