吐槽:stm32的hal库串口接收好复杂啊
那么多函数,跳来跳去的,看了原子和野火的教程,看了一晚上都没整明白 马 ***&&&&& 衡&&&&%%%俞?FDCAN更复杂 不复杂啊,不用中断方式,HAL_UART_Receive()填好串口、接收的缓冲区和准备接收的长度,再加上超时时间,就行了。
用中断方式,HAL_UART_Receive_IT()同样填好参数,在实现HAL_UART_RxCpltCallback()就可以了,Callback会在接收完中断里调用。 野火的不用看就是垃圾,用过他家的F7开发板。HAL库一定要搭配cubemx使用,可以看看硬汉的HAL库教程,目前发现他家的HAL库教程做的比较好。原子的没看过不好说。 用LL库吧,自己写 不是复杂, 而是缺少引导, 部分地方用了很多table可能会比较占用RAM空间 会用cubemx配置串口参数生成初始化代码,知道用哪个函数收发就行。代码里怎么跳根本不用关心。, zzipeng 发表于 2022-3-28 11:17
马 ***&&&&& 衡&&&&%%%俞?
FDCAN更复杂
(引用自2楼)
最近我也在用STM32G4的FDCAN,寄存器写的,不到200行代码,完成基本收发。 先读官方的使用说明,比如F1的UM1850: Description of STM32F1 HAL and low-layer drivers,有How to use this driver章节 我是把hal库的串口中断用自己的代码接管了,自己写环形buffer。等于是只用了hsl库的串口初始化代码。 可以直接把armfly开发板得程序复制进来用,很方便。 这么些年用st、nxp、freescale、silabs等都是自己写驱动。
各种官方库做实验还行,做产品看着看着就膈应。 hal库封装了很多层,要弄明白得慢慢看 HAL会用就好,不要深究到底层。如果深究底层,用标准库或寄存器 SUPER_CRJ 发表于 2022-3-28 11:35
最近我也在用STM32G4的FDCAN,寄存器写的,不到200行代码,完成基本收发。
(引用自8楼)
这么牛,不如贡献下。。口讲无PIN 我觉得水平再高也不要说别人写的代码是垃圾吧,每个人都有好多自己不会而别人会的东西 zzipeng 发表于 2022-6-21 12:29
这么牛,不如贡献下。。口讲无PIN
(引用自15楼)
/**
* @brief CAN_FD(CAN with Flexible Data rate:灵活的dataRate),最终,终于还是来到了CAN-FD芯片!FD-CAN是下个时代的东西,有必要先下手!
* @Author CAN FD其实就是1点:可变波特率+长数据,其它没有了!
另外:务必使用同一套上位机软件!
G4的CAN_FD真是花时间,有点难!主要是:发送和接收的方式都更改了,有点难懂,还有就是:
警告:STM32G4的CAN_FD完全可以参考:STM32H7的FD_CAN,H7的FD-CAN资料比较多!而且都是中文!
*/
#include "G4_CANFD.h"
#include "stdlib.h"
_CAN_RECE canRece;
_canComCount canComCount;
// G4果然NB,尤其发送与接收的操作,也是非常简单。而且直接兼容标准CAN模式!
// 发现:确实可以配置两个波特率,在不跳变的时候,数据段使用的是:仲裁段的配置。
/**
CAN FD总线初始化
常见波特率:
仲裁断
TBS1 TBS2 BRP @80MHZ时钟
"5k": 68 9 199 87.5%
"10k": 26 3 249 87.5%
"20k": 68 9 49 87.5%
"25k": 26 3 99 87.5%
"40k": 33 4 49 87.5%
"50k": 26 3 49 87.5%
"100k":26 3 24 87.5%
"125k":26 3 19 87.5%
"250k":26 3 9 87.5%
"500k":26 3 4 87.5%
"800k":13 4 4 75.0%
"1000k": 13 4 3 75.0%
数据段:仅:125K-5M计算! 再低就没有数据了!
"125k":26 3 19 87.5%
"250k":26 3 9 87.5%
"500k":26 3 4 87.5%
"800k":13 4 4 75.0%
"1000k": 13 4 3 75.0%
"2000k": 4 1 4 75.0%
"4000k": 13 4 0 75.0%
"5000k": 10 3 0 75.0%
*/
// 都设置成500K的波特率
// 时钟认为是80MHZ(涉及到两个时钟域)
// 频率计算公式:FCLK/[(BRP+1)(TBS1+TBS2+3)]
// 采样点:(1+BS1)/(3+BS1+BS2)注:上面的值为寄存器中的值,所以需要进行+1运算以及加上1上同步字节!
void canFDInit( u8 canMode, // CAN模式,暂时保留!
u8 aSJW,u8 aBS1,u8 aBS2,u16 aBRP,// 仲裁断占用BRP:9位,最大值:511,tBS1:8位/255,tBS2:7位/127
u8 dSJW,u8 dBS1,u8 dBS2,u8 dBRP )// 数据段占用BRP:5位,最大值:31,dBS1:5位/31,dBS2:4位/15
{
{ // GPIO初始化
// RCC->AHB2ENR.GPIOAEn_RW = 1;
//
// GPIOA->MODER.ModeCFG11_RW = GPIO_MODER_ALTERNATE;
// GPIOA->OTYPER.OutputType11_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
// GPIOA->OSPEEDR.OutputSpeed11_RW = GPIO_OSPEEDR_HIGH_SPEED;
// GPIOA->PUPDR.PullUpDownCFG11_RW = GPIO_PUPDR_NO_PULL;
// GPIOA->AFRH.AlternateFun11_RW = 9; // AF9:FD-CAN,也是用的AF9功能
// GPIOA->ODR.OutputData11_RW = 1;
//
// GPIOA->MODER.ModeCFG12_RW = GPIO_MODER_ALTERNATE;
// GPIOA->OTYPER.OutputType12_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
// GPIOA->OSPEEDR.OutputSpeed12_RW = GPIO_OSPEEDR_HIGH_SPEED;
// GPIOA->PUPDR.PullUpDownCFG12_RW = GPIO_PUPDR_NO_PULL;
// GPIOA->AFRH.AlternateFun12_RW = 9;// AF9:FD-CAN
// GPIOA->ODR.OutputData12_RW = 1;
RCC->AHB2ENR.GPIOBEn_RW = 1;
GPIOB->MODER.ModeCFG8_RW = GPIO_MODER_ALTERNATE;
GPIOB->OTYPER.OutputType8_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
GPIOB->OSPEEDR.OutputSpeed8_RW = GPIO_OSPEEDR_HIGH_SPEED;
GPIOB->PUPDR.PullUpDownCFG8_RW = GPIO_PUPDR_NO_PULL;
GPIOB->AFRH.AlternateFun8_RW = 9; // AF9:FD-CAN,也是用的AF9功能
GPIOB->ODR.OutputData8_RW = 1;
GPIOB->MODER.ModeCFG9_RW = GPIO_MODER_ALTERNATE;
GPIOB->OTYPER.OutputType9_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
GPIOB->OSPEEDR.OutputSpeed9_RW = GPIO_OSPEEDR_HIGH_SPEED;
GPIOB->PUPDR.PullUpDownCFG9_RW = GPIO_PUPDR_NO_PULL;
GPIOB->AFRH.AlternateFun9_RW = 9;// AF9:FD-CAN
GPIOB->ODR.OutputData9_RW = 1;
}
{ // CAN总线初始化
// CAN总线时钟:
RCC->CCIPR.FDCANCLKSel_RW = 0x01;// PLLQ被选择为:FD_CAN的时钟!
RCC->APB1ENR1.FDCANEn_RW = 1; // 这个时钟对应的地址:0x4000A400,调试模式下和手册指示不一样,手册指示为:msgRam值!
// 而且似乎好像CAN_FD1没有时钟开关?这个只是用来开MsgRam的!
RCC->APB1RSTR1.FDCANRst_RW = 1;
RCC->APB1RSTR1.FDCANRst_RW = 0;
{ //
FDCAN1->CCCR.init_RW = 1;
while( FDCAN1->CCCR.init_RW != 1 ); // 手册表明写1可能存在延迟,所以这里需要确认这个值!
FDCAN1->CCCR.cfgChangeEn_RW = 1;
FDCAN1->CCCR.disableAutoRetransmisson_RW = 1; // 禁止重发,否则会重发到死机!
FDCAN_CONFIG->inputCLKDiv_RW = 0; // 时钟不分频!
}
{ // 波特率的设置
FDCAN1->CCCR.FDOperationEn_RW = 1;
FDCAN1->CCCR.FDCANBitRateSwitching_RW = 1; // 使能可变波特率,注意:也需要配合txBuffer中参数
{ // 普通频率:500K,高速频率:5MB的设置
// 注意:CCCR中的值记得要改!
FDCAN1->NBTP.nominalSynJumpWidth_RW = aSJW;
FDCAN1->NBTP.bitRatePrescaler_RW = aBRP;
FDCAN1->NBTP.nominalTimSegBeforeSamplePoint1_RW = aBS1;
FDCAN1->NBTP.nominalTimSegAfterSamplePoint2_RW = aBS2;
FDCAN1->DBTP.transDelayCompensation_RW = 0; // 禁止延迟!
FDCAN1->DBTP.synchJumpWidth_RW = dSJW; // SJW<=TSEG2,必须这样设定!
FDCAN1->DBTP.dataBitRatePrescaler_RW = dBRP;
FDCAN1->DBTP.dataTimSegBeforeSamplePoint1_RW = dBS1;
FDCAN1->DBTP.dataTimSegAfterSamplePoint2_RW = dBS2;
}
}
{ // 设置过滤器,设置一个可以通过所有标准帧的ID过滤器
// 其实过滤器不需要在init模式下配置?任何时候都可以配置?
// 可以看到,过滤器分为两种,一个是标准帧一个是扩展帧的。
memset( (void*)CANFD1_RAM,0,sizeof(_CANFD_RAM) ); // 先清空所有配置
CANFD1_RAM->standardFilter.standardFilterType_RW = 0x02; // 经典的屏蔽位模式
CANFD1_RAM->standardFilter.standardFilterElementCfg_RW = 0x01; // 存储在FIFO0中
CANFD1_RAM->standardFilter.standardFilterID1_RW = 0x00; // ID
CANFD1_RAM->standardFilter.standardFilterID2_RW = 0x00; // MSAK
CANFD1_RAM->extendedFilter.extendedFilterElementCfg_RW = 0x01; // 存储在FIFO0中
CANFD1_RAM->extendedFilter.extendedFilterType_RW = 0x02;// 经典的屏蔽位模式
CANFD1_RAM->extendedFilter.extendedFilterID1_RW = 0;
CANFD1_RAM->extendedFilter.extendedFilterID2_RW = 0;
}
FDCAN1->RXGFC.listSizeExtended_RW = 8; // 表明使能8个扩展过滤
FDCAN1->RXGFC.listSizeStandard_RW = 28;// 表明使能28个标准过滤
FDCAN1->RXGFC.FIFO0OperationMode_RW = 0; // 工作在OVERWRITE模式下
FDCAN1->RXGFC.FIFO1OperationMode_RW = 0;
FDCAN1->RXGFC.acceptNonMatchingFramesStandard_RW = 2; // 任何都不匹配的标准帧放到哪里 - 可以看出:不匹配的数据都可以拿出来,非常方便调试,也强大!
FDCAN1->RXGFC.acceptNonMatchingFramesExtended_RW = 2; // 不匹配的数据都拒绝!
FDCAN1->RXGFC.rejectRomoteFramesStandard_RW = 0; // 保留标准帧的远程帧
FDCAN1->RXGFC.rejectRomoteFramesExtended_RW = 0;
{ // 使能中断
FDCAN1->IE.rxFIFO0NewMsgEn_RW = 1; // RXFIFO0接收中断使能
FDCAN1->IE.rxFIFO1NewMsgEn_RW = 1;
FDCAN1->IE.busOffStatusEn_RW = 1; // 总线掉线中断
FDCAN1->ILE.enIntLine0_RW = 1; // 使能两个中断
FDCAN1->ILE.enIntLine1_RW = 1;
NVIC_EnableIRQ( FDCAN1_IT0_IRQn );
NVIC_EnableIRQ( FDCAN1_IT1_IRQn );
}
{ // 发送设置!
FDCAN1->TXBC.txFIFOQueueMode_RW = 0; // TX FIFO模式
}
// 关闭初始化,启用CAN总线
FDCAN1->CCCR.cfgChangeEn_RW = 0;
FDCAN1->CCCR.init_RW = 0;
while(FDCAN1->CCCR.init_RW == 0); // 这里也用同步检查下吧
}
}
/**
* @brief 过滤器设置,实测发现:可以在运行的时候直接更改过滤器的设置!
*/
void CANFilterConfiguration( u8 type,u8 index,u32 SF0,u32 SF1 )
{ // 程序设置中比较简单!最好配置:USB_CAN-FD上位机进行运算,或者自己根据手册来运算!
if( type == 0 ){ // 标准设置
CANFD1_RAM->standardFilter.all = SF0;
}
else{ // 扩展设置
CANFD1_RAM->extendedFilter.F0all = SF0;
CANFD1_RAM->extendedFilter.F1all = SF1;
}
}
/**
baudRate:波特率,采用查询法,把常见的波特率都写入进去!这样可以非常方便二次开发
仲裁常用波特率:
如何测试:
1:普通CAN - 500K测试!(因为调试器:500K波特率!)
2:可变:500K+4M测试!(用于测试高速!)
关于:BRS位,指示波特率有没有突变。其实从硬件层面来说:硬件控制器会通过此位决定数据段要不要启用新的采样。
分析下来:即使使用同样波特率,BRS指示为可变波特率问题也应该不大!
但是:最好还是按照标准来,毕竟是标准的调试器!
关于模式:选择,FD模式,变波特率!实际发送我接收的时候再操作!
*/
void canfdInit( u8 mode,u32 arbitrationRate,u32 dataRate )
{
{ // GPIO初始化
RCC->AHB2ENR.GPIOAEn_RW = 1;
GPIOA->MODER.ModeCFG11_RW = GPIO_MODER_ALTERNATE;
GPIOA->OTYPER.OutputType11_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
GPIOA->OSPEEDR.OutputSpeed11_RW = GPIO_OSPEEDR_HIGH_SPEED;
GPIOA->PUPDR.PullUpDownCFG11_RW = GPIO_PUPDR_NO_PULL;
GPIOA->AFRH.AlternateFun11_RW = 9; // AF9:FD-CAN,也是用的AF9功能
GPIOA->ODR.OutputData11_RW = 1;
GPIOA->MODER.ModeCFG12_RW = GPIO_MODER_ALTERNATE;
GPIOA->OTYPER.OutputType12_RW = GPIO_OTYPER_OUTPUT_PUSH_PULL;
GPIOA->OSPEEDR.OutputSpeed12_RW = GPIO_OSPEEDR_HIGH_SPEED;
GPIOA->PUPDR.PullUpDownCFG12_RW = GPIO_PUPDR_NO_PULL;
GPIOA->AFRH.AlternateFun12_RW = 9;// AF9:FD-CAN
GPIOA->ODR.OutputData12_RW = 1;
}
{ // CAN总线初始化
// CAN总线时钟:
RCC->CCIPR.FDCANCLKSel_RW = 0x01;// PLLQ被选择为:FD_CAN的时钟!
RCC->APB1ENR1.FDCANEn_RW = 1; // 这个时钟对应的地址:0x4000A400,调试模式下和手册指示不一样,手册指示为:msgRam值!
// 而且似乎好像CAN_FD1没有时钟开关?这个只是用来开MsgRam的!
RCC->APB1RSTR1.FDCANRst_RW = 1;
RCC->APB1RSTR1.FDCANRst_RW = 0;
{ //
FDCAN1->CCCR.init_RW = 1;
while( FDCAN1->CCCR.init_RW != 1 ); // 手册表明写1可能存在延迟,所以这里需要确认这个值!
FDCAN1->CCCR.cfgChangeEn_RW = 1;
FDCAN1->CCCR.disableAutoRetransmisson_RW = 1; // 禁止重发,否则会重发到死机!
FDCAN_CONFIG->inputCLKDiv_RW = 0; // 时钟不分频!
}
switch( mode )
{ // 模式的组成:CCCR设置及TX BUFFER中的配合
// 有几种组合,注意参数!
case 0: // 经典CAN
FDCAN1->CCCR.FDOperationEn_RW = 0; // 经典模式,只要这个为0,其他设置会被忽略
{ // 都设置成500K的波特率
// 时钟认为是80MHZ(涉及到两个时钟域)
// 频率计算公式:FCLK/[(BRP+1)(TBS1+TBS2+3)]
// 采样点:(1+BS1)/(3+BS1+BS2)注:上面的值为寄存器中的值,所以需要进行+1运算以及加上1上同步字节!
FDCAN1->NBTP.nominalSynJumpWidth_RW = 0;
FDCAN1->NBTP.bitRatePrescaler_RW = 9;
FDCAN1->NBTP.nominalTimSegBeforeSamplePoint1_RW = 12;
FDCAN1->NBTP.nominalTimSegAfterSamplePoint2_RW = 1;
FDCAN1->DBTP.transDelayCompensation_RW = 0; // 禁止延迟!
FDCAN1->DBTP.dataBitRatePrescaler_RW = 9;
FDCAN1->DBTP.dataTimSegBeforeSamplePoint1_RW = 12;
FDCAN1->DBTP.dataTimSegAfterSamplePoint2_RW = 1;
FDCAN1->DBTP.synchJumpWidth_RW = 0; // SJW<=TSEG2,必须这样设定!
}
{ // 普通频率:500K,高速频率:5MB的设置
// 注意:CCCR中的值记得要改!
FDCAN1->NBTP.nominalSynJumpWidth_RW = 0;
FDCAN1->NBTP.bitRatePrescaler_RW = 9;
FDCAN1->NBTP.nominalTimSegBeforeSamplePoint1_RW = 12;
FDCAN1->NBTP.nominalTimSegAfterSamplePoint2_RW = 1;
FDCAN1->DBTP.transDelayCompensation_RW = 0; // 禁止延迟!
FDCAN1->DBTP.dataBitRatePrescaler_RW = 1;
FDCAN1->DBTP.dataTimSegBeforeSamplePoint1_RW = 4;
FDCAN1->DBTP.dataTimSegAfterSamplePoint2_RW = 1;
FDCAN1->DBTP.synchJumpWidth_RW = 0; // SJW<=TSEG2,必须这样设定!
FDCAN1->CCCR.FDOperationEn_RW = 1;
FDCAN1->CCCR.FDCANBitRateSwitching_RW = 1; // 使能可变波特率,注意:也需要配合txBuffer中参数
}
switch( arbitrationRate )
{
}
switch( dataRate )
{
}
break;
case 1: // CAN FD 不变波特率
FDCAN1->CCCR.FDOperationEn_RW = 1;
FDCAN1->CCCR.FDCANBitRateSwitching_RW = 0;
break;
case 2: // CAN FD 变波特率
FDCAN1->CCCR.FDOperationEn_RW = 1;
FDCAN1->CCCR.FDCANBitRateSwitching_RW = 1; // 使能可变波特率,注意:也需要配合txBuffer中参数
break;
}
{ // 设置过滤器,设置一个可以通过所有标准帧的ID过滤器
// 其实过滤器不需要在init模式下配置?任何时候都可以配置?
memset( (void*)CANFD1_RAM,0,sizeof(_CANFD_RAM) ); // 先清空所有配置
CANFD1_RAM->standardFilter.standardFilterType_RW = 0x02; // 经典的屏蔽位模式
CANFD1_RAM->standardFilter.standardFilterElementCfg_RW = 0x01; // 存储在FIFO0中
CANFD1_RAM->standardFilter.standardFilterID1_RW = 0x00; // ID
CANFD1_RAM->standardFilter.standardFilterID2_RW = 0x00; // MSAK
}
FDCAN1->RXGFC.listSizeExtended_RW = 8; // 表明使能8个扩展过滤
FDCAN1->RXGFC.listSizeStandard_RW = 28;// 表明使能28个标准过滤
FDCAN1->RXGFC.FIFO0OperationMode_RW = 0; // 工作在OVERWRITE模式下
FDCAN1->RXGFC.FIFO1OperationMode_RW = 0;
FDCAN1->RXGFC.acceptNonMatchingFramesStandard_RW = 1; // 任何都不匹配的标准帧放到哪里 - 可以看出:不匹配的数据都可以拿出来,非常方便调试,也强大!
FDCAN1->RXGFC.acceptNonMatchingFramesExtended_RW = 1;
FDCAN1->RXGFC.rejectRomoteFramesStandard_RW = 0; // 保留标准帧的远程帧
FDCAN1->RXGFC.rejectRomoteFramesExtended_RW = 0;
{ // 使能中断
FDCAN1->IE.rxFIFO0NewMsgEn_RW = 1; // RXFIFO0接收中断使能
FDCAN1->IE.rxFIFO1NewMsgEn_RW = 1;
FDCAN1->IE.busOffStatusEn_RW = 1; // 总线掉线中断
FDCAN1->ILE.enIntLine0_RW = 1; // 使能两个中断
FDCAN1->ILE.enIntLine1_RW = 1;
NVIC_EnableIRQ( FDCAN1_IT0_IRQn );
NVIC_EnableIRQ( FDCAN1_IT1_IRQn );
}
{ // 发送设置!
FDCAN1->TXBC.txFIFOQueueMode_RW = 0; // TX FIFO模式
}
// 关闭初始化,启用CAN总线
FDCAN1->CCCR.cfgChangeEn_RW = 0;
FDCAN1->CCCR.init_RW = 0;
while(FDCAN1->CCCR.init_RW == 0); // 这里也用同步检查下吧
}
}
/**
CAN 发送函数,用于发送数据!
*/
u8 canSendMsg( void )
{
{ // 简单设置一个FIFO用于测试!
// 先复制一个数据,
CANFD1_RAM->txBufferElement.std.standardID_RW = 0x123;
CANFD1_RAM->txBufferElement.std.isExtendedID_RW = 0;
CANFD1_RAM->txBufferElement.std.remoteReq_RW = 0;
CANFD1_RAM->txBufferElement.std.errorStateIndicator_RW = 0;
CANFD1_RAM->txBufferElement.dataLen_RW = 8;
CANFD1_RAM->txBufferElement.bitrateSwitch_RW = 0;
CANFD1_RAM->txBufferElement.FDFormat_RW = 0;
CANFD1_RAM->txBufferElement.eventFifoCtl_RW = 0;
CANFD1_RAM->txBufferElement.msgMarker_RW = 0;
for( u8 i = 0;i<16;i++ ){
CANFD1_RAM->txBufferElement.data_RW = i;
}
{ // 使能发送操作,G4的发送操作让人感觉到奇怪!
FDCAN1->TXBAR.addRep_RW = 0x01;
}
}
return 0;
}
/**
如何设定发送函数比较好?
1:注意,FD模式下,没有远程帧。
2:len的参数为:9-15,分别对应:12/16/20/24/32/48/64长度!(这日后应该可以找到一些对应关系!)
3:暂时采用一些简单的方案!
下面为发送相关的表格!
BRSE FDOE FDF BRS Frame transmission
Ignored 0 IgnoredIgnored Classic CAN
0 1 0 Ignored Classic CAN
0 1 1 Ignored FD without bit rate switching
1 1 0 Ignored Classic CAN
1 1 1 0 FD without bit rate switching
1 1 1 1 FD with bit rate switching
*/
void canTxMsg( u8 FDFlag,u8 brsFlag,u8 idFormat,u8 dataFormat,u32 id,u8* buf,u8 len )
{
canComCount.canSendOrderCount++;
if( idFormat == 0 ){ // 标准帧
CANFD1_RAM->txBufferElement.std.standardID_RW = id;
CANFD1_RAM->txBufferElement.std.isExtendedID_RW = 0;
CANFD1_RAM->txBufferElement.std.remoteReq_RW = dataFormat;
}
else{ // 扩展帧
CANFD1_RAM->txBufferElement.exd.extendedID_RW = id;
CANFD1_RAM->txBufferElement.exd.isExtendedID_RW = 1;
CANFD1_RAM->txBufferElement.exd.remoteReq_RW = dataFormat;
}
CANFD1_RAM->txBufferElement.std.errorStateIndicator_RW = 0;
CANFD1_RAM->txBufferElement.dataLen_RW = len;
// 发送模式直接根据配置中进行,当然,也可以不根据里面进行!
// 实际需要看情况!因为配置和发送可以分开,有组合表参考,如果接收也可以的话,那就是非常好的!
// 事实发现:好像确实可以先设置总体的为:可变,FD,实际发送和接收的时候再进行判断!
// 如果按照这个发现,则是非常好的!可以简化发送!所以比较建议!
CANFD1_RAM->txBufferElement.FDFormat_RW = FDFlag ;
CANFD1_RAM->txBufferElement.bitrateSwitch_RW = brsFlag;
CANFD1_RAM->txBufferElement.eventFifoCtl_RW = 0;
CANFD1_RAM->txBufferElement.msgMarker_RW = 0;
for( u8 i = 0;i<16;i++ ){ // 不管怎么样,都拷贝64个数据!
CANFD1_RAM->txBufferElement.data_RW = (buf<<0) + (buf<<8) + (buf<<16) + (buf<<24);
}
{ // 使能发送操作,也就是加入命令!
// 此代码里面,暂时只使用一个进行发送!日后再考虑进行更新程序!
// 可以发现:当里面数据从1变为0的时候,表示发送完成!
FDCAN1->TXBAR.addRep_RW = 0x01;
u32 count = 0xFFFFF;
while( (FDCAN1->TXBTO.transOccurred_R == 0)&&( FDCAN1->TXBCF.cancellFinished_R == 0 )&&( count-- != 0 ));
if( FDCAN1->TXBTO.transOccurred_R != 0 ){
canComCount.canSendSucessCount++;
}
else{
canComCount.canSendFailCount++;
canComCount.lastErrorCode = FDCAN1->PSR.All&0x07; // 应该出错了!
}
}
}
/**
中断处理函数!中断线,可以都配置到一起,需要注意!
*/
void FDCAN1_IT0_IRQHandler( void )
{
if( FDCAN1->IR.rxFIFO0NewMsg_RCW1 ){ // 接收到了数据
FDCAN1->IR.rxFIFO0NewMsg_RCW1 = 1; // 清除中断
{ // 这个应该如何操作?因为实际上FIFO数据,先接收到缓冲区。然后在前台进行处理!
// 直接拷贝数据!
// 由于采用了RAM方式,系统可能不知道你什么读出的,或者读出几次,所以读出的方法会相对麻烦点。
do{
memcpy(&canRece.buf,(void *)&CANFD1_RAM->RXFIFO0,sizeof(_MSGRAM_RX_FIFO));
canRece.pWrite++;
canRece.pWrite %= CAN_RX_MSG_LEN;
canComCount.canRecvSucessCount++;
FDCAN1->RXF0A.rxFIFOAckIndex_RW = FDCAN1->RXF0S.rxFIFOGetIndex_R; // 写当前指针可以加1处理,最后再进行判断!
}while( FDCAN1->RXF0S.rxFIFOFillLev_R != 0 ); // 如果里面有数据则一直读出
}
}
if( FDCAN1->IR.busOffStatus_RCW1 == 1 ){
// bus off,不同于STM32F1有自己离线恢复,G4没有发现离线恢复!
// 暂时采用简单的方法:直接恢复总线初始化!
FDCAN1->CCCR.init_RW = 0; // OFF状态下关闭总线,所以直接恢复就行,系统会自动恢复!
FDCAN1->IR.busOffStatus_RCW1 = 1;
}
}
/**
第二中断线,暂时没有使用到!
*/
void FDCAN1_IT1_IRQHandler( void )
{
}
zzipeng 发表于 2022-6-21 12:29
这么牛,不如贡献下。。口讲无PIN
(引用自15楼)
我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。 SUPER_CRJ 发表于 2022-6-21 13:46
我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。 ...
(引用自18楼)
{:handshake:} ,牛逼,膜拜 HAL库封装得太厉害了。标准库最适中,奈何现在新芯片都得HAL或LL 印度三哥对HAL 贡献良多 hal库就是一坨屎 polarbear 发表于 2022-6-21 15:22
印度三哥对HAL 贡献良多
(引用自21楼)
难怪HAL像一堆屎,原来是牛粪都觉得香喷喷的人来写的。 跟电脑软件一样,复杂了以后只能直接调用。 本帖最后由 jingwaner 于 2022-6-21 22:27 编辑
用寄存器编程,磨刀不误砍柴工,单片机就是这么玩的。
HAL库害人不浅,ST的奴隶一大堆。。。
个人感觉 STM32-HAL 的问题不在于难用,而是封闭的生态导致只支持 STM32...
如果有一天 STM32 涨价/没货导致更换 CPU,那废老大劲整明白的 HAL 抽象在哪?移植性在哪?还不是全都重来...
论 HAL,只服 Arduino 等一众开源平台。 jingwaner 发表于 2022-6-21 22:24
用寄存器编程,磨刀不误砍柴工,单片机就是这么玩的。
HAL库害人不浅,ST的奴隶一大堆。。。
(引用自25楼)
是的,不能太依赖HAL库,而且项目核心功能构造优化才是最花精力的。HAL只是开始省点事,一旦有问题,找起来也要不少工夫 现在的芯片厂家都把电工给惯坏了, 想想刚入行时都是对着手册一个个寄存器一个个位来搞, IC厂家哪有什么驱动库提供啊. SUPER_CRJ 发表于 2022-6-21 13:46
我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。 ...
(引用自18楼)
搭个车,歪个楼,有没有H750VB的USART DMA加空闲中断不定长接收的代码样本?我自己照网上搞的老是不太对,都不知道哪里出来问题? 上传一个,我一直在用的,自已觉得还是比较好用的
#ifndef __BSP_USART1_H__
#define __BSP_USART1_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#ifdef __cplusplus
}
#endif
void USART1_SendChar(unsigned char ch);
void USART1_SendBuff(unsigned char *p,int L);
void USART1_SendStr(char *p);
void USART1_SendProcess(void);
unsigned int USART1_CheckLen(void);
unsigned char USART1_ReadByte(void);
unsigned char USART1_CheckByte(unsigned short n);
void USART1_Discard(unsigned short n);
void USART1_Config(uint32_t BaudRate);
void USART1_Test(void);
#endif /* __USART1_H */
#include "main.h"
/*--------------------环形缓存 发送-----------------------------*/
static const unsigned intTxLen=1024;
static volatile unsigned char TxBuf;
static volatile unsigned char* TxW=TxBuf;
static volatile unsigned char* TxR=TxBuf;
static unsigned int TxCheckLen(void)
{
unsigned int Len; //short
volatile unsigned char *W=TxW; volatile unsigned char *R=TxR;
if(W>=R)Len=W-R;else Len=(W+TxLen)-R; //这样正确(中途中断改变也变不了结果)
return Len;
}
static unsigned char TxReadByte(void)
{
unsigned char R=*TxR; //读数
if(TxR!=TxW){ if(TxR+1>=(TxBuf+TxLen))TxR =TxBuf; else TxR++;}//下标
return R;
}
void USART1_SendProcess(void)
{//发送缓存,有数据就全部发送出去
unsigned char ch;
while(TxCheckLen()){
ch = TxReadByte();
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch );
}
}
void USART1_SendChar(unsigned char ch) //将命令压入缓存等待发送
{
volatile unsigned char *W=TxW; //这里要与上面指针相同
W=TxW+1; if(W>=TxBuf+TxLen)W=TxBuf; //取下一位置(到顶转到底)
if(W!=TxR){*TxW=ch; TxW=W; }
}
// void USART1_SendChar(unsigned char ch)//发送一位数锯
// {
// /* 等待发送完毕 */
// while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// USART_SendData(USART1, ch );
// }
void USART1_SendBuff(unsigned char *p,int L)
{
unsigned int len = L;
while (len>0){
USART1_SendChar(*p++);
len--;
}
}
//void USART1_SendBuff(unsigned char *p,int L) //直接发送
//{
// SCUART_Write(UART1, p, L); //发送给串口
//}
void USART1_SendStr(char *p)//发送字符串
{
while (*p)
{
USART1_SendChar(*p++);
}
}
/*--------------------环形缓存-----------------------------*/
static const unsigned intRxLen=1024;
static volatile unsigned char RxBuf;
static volatile unsigned char* RxW=RxBuf;
static volatile unsigned char* RxR=RxBuf;
void USART1_IRQHandler(void)//中断接收
{
volatile unsigned char *W=RxW; //这里要与上面指针相同
uint8_t ch;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ch = USART_ReceiveData(USART1);
W=RxW+1; if(W>=RxBuf+RxLen)W=RxBuf; //取下一位置(到顶转到底)
if(W!=RxR){*RxW=ch; RxW=W; }
}
}
unsigned int USART1_CheckLen(void) //检查RX接收了多少数据
{
unsigned int Len; //short
volatile unsigned char *W=RxW; volatile unsigned char *R=RxR;
if(W>=R)Len=W-R;else Len=(W+RxLen)-R; //这样正确(中途中断改变也变不了结果)
// if( Rx2W>=Rx2R){ Len=Rx2W-Rx2R;}else { Len=Rx2Len+Rx2W-Rx2R; }//接收了多少数据
return Len;
}
unsigned char USART1_ReadByte(void) //读RX中数锯,地指加一,和丢弃
{
unsigned char R=*RxR; //读数
if(RxR!=RxW){ if(RxR+1>=(RxBuf+RxLen))RxR =RxBuf; else RxR++;}//下标
return R;
}
unsigned char USART1_CheckByte(unsigned short n) //看RX中数锯,地指不变,
{
volatile unsigned char *R=RxR+n;
if(R>=(RxBuf+RxLen))R-=RxLen;
return *R;
}
void USART1_Discard(unsigned short n) //丢弃RX数据几位
{
while(n){
n--;
if(RxR==RxW) return;
if(RxR+1>=RxBuf+RxLen){RxR=RxBuf;} else RxR++; //下标
}
}
/*-------------------- 初始化 -----------------------------*/
void USART1_Config(uint32_t BaudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义结端口构体
USART_InitTypeDef USART_InitStructure;//定义结串口构体
NVIC_InitTypeDef NVIC_InitStructure;//定义中断结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//macUSART1_CLK
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//macUSART1_GPIO_CLK
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);//
/*2、配置接口:接收端 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);//
/*3、配置串口USART参数: */
USART_InitStructure.USART_BaudRate = BaudRate; //波特率 115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据字长---8 字长8bit://带奇偶校验时字长为9bit,无奇偶校验时字长为8bit
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位-----1
USART_InitStructure.USART_Parity = USART_Parity_No; //校验-------无校验USART_Parity_No
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//模式-------收发
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制-无
USART_Init(USART1, &USART_InitStructure); //初始化串口3
/*4、配置中断NVIC参数: */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //选择优先级组1 ==主优先级1位(0-1)+副优先级3位(0-7)
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //USART1全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级0,1,2,3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能USART1通道
NVIC_Init(&NVIC_InitStructure); //初始化NVIC
/*5、初始化缓冲:*/
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//先使能接收中断;(打开这允许接收,同时也开了ORE )
USART_ClearFlag(USART1, USART_FLAG_TC);//清除移发送位传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
USART_ITConfig(USART1, USART_IT_TC, DISABLE); //关闭TC中断
USART_Cmd(USART1, ENABLE); //使能--串口3
USART1_SendStr("USART1_Config is OK!\r\n");
}
//*************** 测试 ***************//
void USART1_Test(void)
{
while(USART1_CheckLen()>0){
USART1_SendChar(USART1_ReadByte());
}
}
/**************************************************************
** 函数名 :fputc
** 功能 :重定向c库函数printf到USART
** 注意 :由printf调用
***************************************************************/
int fputc(int ch, FILE *f)//模拟
{
USART_SendData(USART1, (unsigned char) ch);/* 将Printf内容发往串口 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); /* 等待发送结束 */
return (ch);
}
/**************************************************************
** 函数名 :fgetc
** 功能 :重定向c库函数scanf到USART
** 注意 :由scanf调用
***************************************************************/
int fgetc(FILE *f)//模拟
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);// 等待串口3输入数据
return (int)USART_ReceiveData(USART1);
}
/*********************************************END OF FILE**********************/
学习了,不知道用啥好,新手 jssd 发表于 2022-7-14 08:57
上传一个,我一直在用的,自已觉得还是比较好用的
(引用自30楼)
1、你这个就是串口接收发送环形队列,对于没上系统的地方可以用。上了系统还是用接收发送队列加DMA传输空闲中断 不定长接收发送好点吧。
2、搭个车,有谁知道我这个帖子最后问的问题吗?https://www.amobbs.com/thread-5598475-2-1.html
页:
[1]