mahengyu 发表于 2022-3-28 11:08:51

吐槽:stm32的hal库串口接收好复杂啊

那么多函数,跳来跳去的,看了原子和野火的教程,看了一晚上都没整明白

zzipeng 发表于 2022-3-28 11:17:21

马 ***&&&&& 衡&&&&%%%俞?
FDCAN更复杂

Doding 发表于 2022-3-28 11:20:20

不复杂啊,不用中断方式,HAL_UART_Receive()填好串口、接收的缓冲区和准备接收的长度,再加上超时时间,就行了。
用中断方式,HAL_UART_Receive_IT()同样填好参数,在实现HAL_UART_RxCpltCallback()就可以了,Callback会在接收完中断里调用。

kitten 发表于 2022-3-28 11:24:56

野火的不用看就是垃圾,用过他家的F7开发板。HAL库一定要搭配cubemx使用,可以看看硬汉的HAL库教程,目前发现他家的HAL库教程做的比较好。原子的没看过不好说。

asj1989 发表于 2022-3-28 11:29:09

用LL库吧,自己写

icoyool 发表于 2022-3-28 11:30:10

不是复杂, 而是缺少引导, 部分地方用了很多table可能会比较占用RAM空间

kitten 发表于 2022-3-28 11:32:36

会用cubemx配置串口参数生成初始化代码,知道用哪个函数收发就行。代码里怎么跳根本不用关心。,

SUPER_CRJ 发表于 2022-3-28 11:35:27

zzipeng 发表于 2022-3-28 11:17
马 ***&&&&& 衡&&&&%%%俞?
FDCAN更复杂
(引用自2楼)

最近我也在用STM32G4的FDCAN,寄存器写的,不到200行代码,完成基本收发。

2nd 发表于 2022-3-28 11:37:18

先读官方的使用说明,比如F1的UM1850: Description of STM32F1 HAL and low-layer drivers,有How to use this driver章节

hecat 发表于 2022-3-28 12:06:45

我是把hal库的串口中断用自己的代码接管了,自己写环形buffer。等于是只用了hsl库的串口初始化代码。

ycwjl728 发表于 2022-3-28 13:44:01

可以直接把armfly开发板得程序复制进来用,很方便。

MasterPhi 发表于 2022-3-28 13:51:27

这么些年用st、nxp、freescale、silabs等都是自己写驱动。

各种官方库做实验还行,做产品看着看着就膈应。

weiwei4 发表于 2022-3-28 16:20:17

hal库封装了很多层,要弄明白得慢慢看

mypear 发表于 2022-3-29 14:14:04

HAL会用就好,不要深究到底层。如果深究底层,用标准库或寄存器

zzipeng 发表于 2022-6-21 12:29:11

SUPER_CRJ 发表于 2022-3-28 11:35
最近我也在用STM32G4的FDCAN,寄存器写的,不到200行代码,完成基本收发。
(引用自8楼)

这么牛,不如贡献下。。口讲无PIN

ibmx311 发表于 2022-6-21 12:57:22

我觉得水平再高也不要说别人写的代码是垃圾吧,每个人都有好多自己不会而别人会的东西

SUPER_CRJ 发表于 2022-6-21 13:45:41

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 )
{
       
}



SUPER_CRJ 发表于 2022-6-21 13:46:53

zzipeng 发表于 2022-6-21 12:29
这么牛,不如贡献下。。口讲无PIN
(引用自15楼)

我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。

zzipeng 发表于 2022-6-21 14:00:53

SUPER_CRJ 发表于 2022-6-21 13:46
我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。 ...
(引用自18楼)

{:handshake:} ,牛逼,膜拜

shuiluo2 发表于 2022-6-21 14:58:23

HAL库封装得太厉害了。标准库最适中,奈何现在新芯片都得HAL或LL

polarbear 发表于 2022-6-21 15:22:10

印度三哥对HAL 贡献良多

luhuaren 发表于 2022-6-21 21:58:56

hal库就是一坨屎

tclg 发表于 2022-6-21 22:04:15

polarbear 发表于 2022-6-21 15:22
印度三哥对HAL 贡献良多
(引用自21楼)

难怪HAL像一堆屎,原来是牛粪都觉得香喷喷的人来写的。

huangqi412 发表于 2022-6-21 22:10:17

跟电脑软件一样,复杂了以后只能直接调用。

jingwaner 发表于 2022-6-21 22:24:32

本帖最后由 jingwaner 于 2022-6-21 22:27 编辑

用寄存器编程,磨刀不误砍柴工,单片机就是这么玩的。
HAL库害人不浅,ST的奴隶一大堆。。。



t3486784401 发表于 2022-6-21 23:37:54

个人感觉 STM32-HAL 的问题不在于难用,而是封闭的生态导致只支持 STM32...
如果有一天 STM32 涨价/没货导致更换 CPU,那废老大劲整明白的 HAL 抽象在哪?移植性在哪?还不是全都重来...

论 HAL,只服 Arduino 等一众开源平台。

flash3g 发表于 2022-6-21 23:43:05

jingwaner 发表于 2022-6-21 22:24
用寄存器编程,磨刀不误砍柴工,单片机就是这么玩的。
HAL库害人不浅,ST的奴隶一大堆。。。

(引用自25楼)

是的,不能太依赖HAL库,而且项目核心功能构造优化才是最花精力的。HAL只是开始省点事,一旦有问题,找起来也要不少工夫

sibin 发表于 2022-6-22 08:27:19

现在的芯片厂家都把电工给惯坏了, 想想刚入行时都是对着手册一个个寄存器一个个位来搞, IC厂家哪有什么驱动库提供啊.

zzipeng 发表于 2022-7-11 21:09:16

SUPER_CRJ 发表于 2022-6-21 13:46
我做的一个USB转CAN FD调试器中用代码直接截取出来了。
不加备注之类,差不多200多行。 ...
(引用自18楼)

搭个车,歪个楼,有没有H750VB的USART DMA加空闲中断不定长接收的代码样本?我自己照网上搞的老是不太对,都不知道哪里出来问题?

jssd 发表于 2022-7-14 08:57:27

上传一个,我一直在用的,自已觉得还是比较好用的


#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**********************/

Roy_wang 发表于 2022-7-19 16:55:48

学习了,不知道用啥好,新手

zzipeng 发表于 2022-7-24 22:17:34

jssd 发表于 2022-7-14 08:57
上传一个,我一直在用的,自已觉得还是比较好用的
(引用自30楼)

1、你这个就是串口接收发送环形队列,对于没上系统的地方可以用。上了系统还是用接收发送队列加DMA传输空闲中断 不定长接收发送好点吧。
2、搭个车,有谁知道我这个帖子最后问的问题吗?https://www.amobbs.com/thread-5598475-2-1.html
页: [1]
查看完整版本: 吐槽:stm32的hal库串口接收好复杂啊