搜索
bottom↓
回复: 31

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

[复制链接]

出0入0汤圆

发表于 2022-3-28 11:08:51 | 显示全部楼层 |阅读模式
那么多函数,跳来跳去的,看了原子和野火的教程,看了一晚上都没整明白

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2022-3-28 11:17:21 | 显示全部楼层
马 ***&&&&& 衡  &&&&%%%俞?
FDCAN更复杂

出0入75汤圆

发表于 2022-3-28 11:20:20 | 显示全部楼层
不复杂啊,不用中断方式,HAL_UART_Receive()填好串口、接收的缓冲区和准备接收的长度,再加上超时时间,就行了。
用中断方式,HAL_UART_Receive_IT()同样填好参数,在实现HAL_UART_RxCpltCallback()就可以了,Callback会在接收完中断里调用。

出0入1209汤圆

发表于 2022-3-28 11:24:56 | 显示全部楼层
野火的不用看就是垃圾,用过他家的F7开发板。HAL库一定要搭配cubemx使用,可以看看硬汉的HAL库教程,目前发现他家的HAL库教程做的比较好。原子的没看过不好说。

出0入131汤圆

发表于 2022-3-28 11:29:09 | 显示全部楼层
用LL库吧,自己写

出0入8汤圆

发表于 2022-3-28 11:30:10 | 显示全部楼层
不是复杂, 而是缺少引导, 部分地方用了很多table可能会比较占用RAM空间

出0入1209汤圆

发表于 2022-3-28 11:32:36 | 显示全部楼层
会用cubemx配置串口参数生成初始化代码,知道用哪个函数收发就行。代码里怎么跳根本不用关心。,

出590入1001汤圆

发表于 2022-3-28 11:35:27 | 显示全部楼层
zzipeng 发表于 2022-3-28 11:17
马 ***&&&&& 衡  &&&&%%%俞?
FDCAN更复杂
(引用自2楼)

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

出15入178汤圆

发表于 2022-3-28 11:37:18 | 显示全部楼层
先读官方的使用说明,比如F1的UM1850: Description of STM32F1 HAL and low-layer drivers,有How to use this driver章节

出0入25汤圆

发表于 2022-3-28 12:06:45 来自手机 | 显示全部楼层
我是把hal库的串口中断用自己的代码接管了,自己写环形buffer。等于是只用了hsl库的串口初始化代码。

出0入24汤圆

发表于 2022-3-28 13:44:01 | 显示全部楼层
可以直接把armfly开发板得程序复制进来用,很方便。

出0入0汤圆

发表于 2022-3-28 13:51:27 来自手机 | 显示全部楼层
这么些年用st、nxp、freescale、silabs等都是自己写驱动。

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

出0入13汤圆

发表于 2022-3-28 16:20:17 | 显示全部楼层
hal库封装了很多层,要弄明白得慢慢看

出0入0汤圆

发表于 2022-3-29 14:14:04 | 显示全部楼层
HAL会用就好,不要深究到底层。如果深究底层,用标准库或寄存器

出0入0汤圆

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

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

出0入300汤圆

发表于 2022-6-21 12:57:22 来自手机 | 显示全部楼层
我觉得水平再高也不要说别人写的代码是垃圾吧,每个人都有好多自己不会而别人会的东西

出590入1001汤圆

发表于 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[0].standardFilterType_RW = 0x02; // 经典的屏蔽位模式
                        CANFD1_RAM->standardFilter[0].standardFilterElementCfg_RW = 0x01; // 存储在FIFO0中
                        CANFD1_RAM->standardFilter[0].standardFilterID1_RW = 0x00; // ID
                        CANFD1_RAM->standardFilter[0].standardFilterID2_RW = 0x00; // MSAK
                       
                        CANFD1_RAM->extendedFilter[0].extendedFilterElementCfg_RW = 0x01; // 存储在FIFO0中
                        CANFD1_RAM->extendedFilter[0].extendedFilterType_RW = 0x02;// 经典的屏蔽位模式
                        CANFD1_RAM->extendedFilter[0].extendedFilterID1_RW = 0;
                        CANFD1_RAM->extendedFilter[0].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[index].all = SF0;
        }
        else{ // 扩展设置
                CANFD1_RAM->extendedFilter[index].F0all = SF0;
                CANFD1_RAM->extendedFilter[index].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[0].standardFilterType_RW = 0x02; // 经典的屏蔽位模式
                        CANFD1_RAM->standardFilter[0].standardFilterElementCfg_RW = 0x01; // 存储在FIFO0中
                        CANFD1_RAM->standardFilter[0].standardFilterID1_RW = 0x00; // ID
                        CANFD1_RAM->standardFilter[0].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[0].std.standardID_RW = 0x123;
                CANFD1_RAM->txBufferElement[0].std.isExtendedID_RW = 0;
                CANFD1_RAM->txBufferElement[0].std.remoteReq_RW = 0;
                CANFD1_RAM->txBufferElement[0].std.errorStateIndicator_RW = 0;
                CANFD1_RAM->txBufferElement[0].dataLen_RW = 8;
                CANFD1_RAM->txBufferElement[0].bitrateSwitch_RW = 0;
                CANFD1_RAM->txBufferElement[0].FDFormat_RW = 0;
                CANFD1_RAM->txBufferElement[0].eventFifoCtl_RW = 0;
                CANFD1_RAM->txBufferElement[0].msgMarker_RW = 0;
                for( u8 i = 0;i<16;i++ ){
                        CANFD1_RAM->txBufferElement[0].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    Ignored  Ignored    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[0].std.standardID_RW = id;
                CANFD1_RAM->txBufferElement[0].std.isExtendedID_RW = 0;
                CANFD1_RAM->txBufferElement[0].std.remoteReq_RW = dataFormat;
        }
        else{ // 扩展帧
                CANFD1_RAM->txBufferElement[0].exd.extendedID_RW = id;
                CANFD1_RAM->txBufferElement[0].exd.isExtendedID_RW = 1;
                CANFD1_RAM->txBufferElement[0].exd.remoteReq_RW = dataFormat;
        }
        CANFD1_RAM->txBufferElement[0].std.errorStateIndicator_RW = 0;
        CANFD1_RAM->txBufferElement[0].dataLen_RW = len;
        // 发送模式直接根据配置中进行,当然,也可以不根据里面进行!
        // 实际需要看情况!因为配置和发送可以分开,有组合表参考,如果接收也可以的话,那就是非常好的!
        // 事实发现:好像确实可以先设置总体的为:可变,FD,实际发送和接收的时候再进行判断!
        // 如果按照这个发现,则是非常好的!可以简化发送!所以比较建议!
        CANFD1_RAM->txBufferElement[0].FDFormat_RW = FDFlag ;
        CANFD1_RAM->txBufferElement[0].bitrateSwitch_RW = brsFlag;
        CANFD1_RAM->txBufferElement[0].eventFifoCtl_RW = 0;
        CANFD1_RAM->txBufferElement[0].msgMarker_RW = 0;
        for( u8 i = 0;i<16;i++ ){ // 不管怎么样,都拷贝64个数据!
                CANFD1_RAM->txBufferElement[0].data_RW = (buf[i*4+0]<<0) + (buf[i*4+1]<<8) + (buf[i*4+2]<<16) + (buf[i*4+3]<<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[canRece.pWrite],(void *)&CANFD1_RAM->RXFIFO0[FDCAN1->RXF0S.rxFIFOGetIndex_R],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 )
{
       
}



出590入1001汤圆

发表于 2022-6-21 13:46:53 | 显示全部楼层
zzipeng 发表于 2022-6-21 12:29
这么牛,不如贡献下。。口讲无PIN
(引用自15楼)

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

出0入0汤圆

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

,牛逼,膜拜

出130入20汤圆

发表于 2022-6-21 14:58:23 | 显示全部楼层
HAL库封装得太厉害了。标准库最适中,奈何现在新芯片都得HAL或LL

出0入59汤圆

发表于 2022-6-21 15:22:10 | 显示全部楼层
印度三哥对HAL 贡献良多

出0入0汤圆

发表于 2022-6-21 21:58:56 来自手机 | 显示全部楼层
hal库就是一坨屎

出0入0汤圆

发表于 2022-6-21 22:04:15 | 显示全部楼层
polarbear 发表于 2022-6-21 15:22
印度三哥对HAL 贡献良多
(引用自21楼)

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

出0入0汤圆

发表于 2022-6-21 22:10:17 | 显示全部楼层
跟电脑软件一样,复杂了以后只能直接调用。

出0入8汤圆

发表于 2022-6-21 22:24:32 | 显示全部楼层
本帖最后由 jingwaner 于 2022-6-21 22:27 编辑

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



出200入2554汤圆

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

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

出0入0汤圆

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

(引用自25楼)

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

出0入0汤圆

发表于 2022-6-22 08:27:19 | 显示全部楼层
现在的芯片厂家都把电工给惯坏了, 想想刚入行时都是对着手册一个个寄存器一个个位来搞, IC厂家哪有什么驱动库提供啊.

出0入0汤圆

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

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

出0入55汤圆

发表于 2022-7-14 08:57:27 | 显示全部楼层
上传一个,我一直在用的,自已觉得还是比较好用的


  1. #ifndef __BSP_USART1_H__
  2. #define        __BSP_USART1_H__

  3. #ifdef __cplusplus  
  4. extern "C" {  
  5. #endif  

  6. #include "main.h"

  7. #ifdef __cplusplus
  8. }
  9. #endif

  10. void USART1_SendChar(unsigned char ch);
  11. void USART1_SendBuff(unsigned char *p,int L);
  12. void USART1_SendStr(char *p);
  13. void USART1_SendProcess(void);

  14. unsigned int USART1_CheckLen(void);
  15. unsigned char USART1_ReadByte(void);
  16. unsigned char USART1_CheckByte(unsigned short n);
  17. void USART1_Discard(unsigned short n);

  18. void USART1_Config(uint32_t BaudRate);
  19. void USART1_Test(void);

  20. #endif /* __USART1_H */
复制代码

  1. #include "main.h"

  2. /*--------------------环形缓存 发送-----------------------------*/
  3. static const unsigned int  TxLen=1024;
  4. static volatile unsigned char TxBuf[TxLen];
  5. static volatile unsigned char* TxW=TxBuf;
  6. static volatile unsigned char* TxR=TxBuf;

  7. static unsigned int TxCheckLen(void)
  8. {   
  9.         unsigned int Len; //short
  10.         volatile unsigned char *W=TxW; volatile unsigned char *R=TxR;
  11.         if(W>=R)Len=W-R;else Len=(W+TxLen)-R;         //这样正确(中途中断改变也变不了结果)
  12.     return Len;
  13. }

  14. static unsigned char TxReadByte(void)
  15. {       
  16.         unsigned char R=*TxR;       //读数
  17.         if(TxR!=TxW){        if(TxR+1>=(TxBuf+TxLen))TxR =TxBuf; else TxR++;}//下标
  18.         return R;
  19. }

  20. void USART1_SendProcess(void)
  21. {//发送缓存,有数据就全部发送出去
  22.         unsigned char ch;
  23.         while(TxCheckLen()){
  24.                 ch = TxReadByte();
  25.                 while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  26.                 USART_SendData(USART1, ch );
  27.         }
  28. }

  29. void USART1_SendChar(unsigned char ch) //将命令压入缓存等待发送
  30. {
  31.         volatile unsigned char *W=TxW; //这里要与上面指针相同
  32.         W=TxW+1; if(W>=TxBuf+TxLen)W=TxBuf;        //取下一位置(到顶转到底)
  33.         if(W!=TxR){*TxW=ch; TxW=W; }
  34. }

  35. // void USART1_SendChar(unsigned char ch)//发送一位数锯
  36. // {       
  37.         // /* 等待发送完毕 */
  38.         // while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  39.         // USART_SendData(USART1, ch );
  40. // }

  41. void USART1_SendBuff(unsigned char *p,int L)
  42. {
  43.         unsigned int len = L;
  44.     while (len>0){
  45.                 USART1_SendChar(*p++);
  46.                 len--;
  47.     }
  48. }

  49. //void USART1_SendBuff(unsigned char *p,int L) //直接发送
  50. //{
  51. //        SCUART_Write(UART1, p, L); //发送给串口
  52. //}

  53. void USART1_SendStr(char *p)//发送字符串
  54. {       
  55.         while (*p)
  56.     {
  57.                 USART1_SendChar(*p++);
  58.     }
  59. }


  60. /*--------------------环形缓存-----------------------------*/
  61. static const unsigned int  RxLen=1024;
  62. static volatile unsigned char RxBuf[RxLen];
  63. static volatile unsigned char* RxW=RxBuf;
  64. static volatile unsigned char* RxR=RxBuf;

  65. void USART1_IRQHandler(void)  //中断接收
  66. {       
  67.         volatile unsigned char *W=RxW; //这里要与上面指针相同
  68.         uint8_t ch;
  69.        
  70.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  71.         {        
  72.                 ch = USART_ReceiveData(USART1);
  73.                 W=RxW+1; if(W>=RxBuf+RxLen)W=RxBuf;        //取下一位置(到顶转到底)
  74.                 if(W!=RxR){*RxW=ch; RxW=W; }
  75.         }
  76. }       

  77. unsigned int USART1_CheckLen(void) //检查RX接收了多少数据
  78. {   
  79.         unsigned int Len; //short
  80.         volatile unsigned char *W=RxW; volatile unsigned char *R=RxR;
  81.         if(W>=R)Len=W-R;else Len=(W+RxLen)-R;         //这样正确(中途中断改变也变不了结果)
  82. //        if( Rx2W>=Rx2R){ Len=Rx2W-Rx2R;}else { Len=Rx2Len+Rx2W-Rx2R; }//接收了多少数据
  83.     return Len;
  84. }

  85. unsigned char USART1_ReadByte(void)   //读RX中数锯,地指加一,和丢弃
  86. {       
  87.         unsigned char R=*RxR;       //读数
  88.         if(RxR!=RxW){        if(RxR+1>=(RxBuf+RxLen))RxR =RxBuf; else RxR++;}//下标
  89.         return R;
  90. }

  91. unsigned char USART1_CheckByte(unsigned short n) //看RX中数锯,地指不变,
  92. {         
  93.         volatile unsigned char *R=RxR+n;
  94.         if(R>=(RxBuf+RxLen))R-=RxLen;
  95.         return *R;
  96. }       

  97. void USART1_Discard(unsigned short n) //丢弃RX数据几位  
  98. {       
  99.         while(n){
  100.                 n--;  
  101.                 if(RxR==RxW) return;
  102.                 if(RxR+1>=RxBuf+RxLen){RxR=RxBuf;} else RxR++; //下标
  103.         }       
  104. }  

  105. /*-------------------- 初始化 -----------------------------*/
  106. void USART1_Config(uint32_t BaudRate)
  107. {
  108.         GPIO_InitTypeDef GPIO_InitStructure;  //定义结端口构体
  109.     USART_InitTypeDef USART_InitStructure;//定义结串口构体
  110.         NVIC_InitTypeDef NVIC_InitStructure;  //定义中断结构体

  111.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);  //macUSART1_CLK
  112. //        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //macUSART1_GPIO_CLK
  113.        
  114.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  115.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;   
  116.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  117.     GPIO_Init(GPIOA, &GPIO_InitStructure);//
  118.         /*2、配置接口:接收端   */
  119.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;   
  120.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;       
  121.     GPIO_Init(GPIOA, &GPIO_InitStructure);//
  122.        
  123.         /*3、配置串口USART参数: */
  124.         USART_InitStructure.USART_BaudRate = BaudRate;                 //波特率 115200         
  125.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;    //数据字长---8 字长8bit://带奇偶校验时字长为9bit,无奇偶校验时字长为8bit                                                                               
  126.         USART_InitStructure.USART_StopBits =   USART_StopBits_1;               //停止位-----1                                                                
  127.         USART_InitStructure.USART_Parity =     USART_Parity_No;                           //校验-------无校验USART_Parity_No
  128.         USART_InitStructure.USART_Mode =       USART_Mode_Rx | USART_Mode_Tx;//模式-------收发
  129.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;        //硬件流控制-无                                       
  130.     USART_Init(USART1, &USART_InitStructure);   //初始化串口3   
  131.         /*4、配置中断NVIC参数:        */
  132.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);                 //选择优先级组1 ==主优先级1位(0-1)+副优先级3位(0-7)
  133.         NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                 //USART1全局中断
  134.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级0
  135.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                 //响应优先级0,1,2,3
  136.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                         //使能USART1通道
  137.         NVIC_Init(&NVIC_InitStructure);                                 //初始化NVIC
  138.        
  139.         /*5、初始化缓冲:*/  
  140.         USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//先使能接收中断;(打开这允许接收,同时也开了ORE )
  141.         USART_ClearFlag(USART1, USART_FLAG_TC);//清除移发送位传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
  142.         USART_ITConfig(USART1, USART_IT_TC, DISABLE); //关闭TC中断
  143.     USART_Cmd(USART1, ENABLE);                                        //使能--串口3
  144.        
  145.         USART1_SendStr("USART1_Config is OK!\r\n");
  146. }

  147. //*************** 测试 ***************//
  148. void USART1_Test(void)
  149. {
  150.         while(USART1_CheckLen()>0){
  151.                 USART1_SendChar(USART1_ReadByte());
  152.         }
  153. }


  154. /**************************************************************
  155. ** 函数名 :fputc
  156. ** 功能   :重定向c库函数printf到USART
  157. ** 注意   :由printf调用
  158. ***************************************************************/
  159. int fputc(int ch, FILE *f)//模拟
  160. {  
  161.         USART_SendData(USART1, (unsigned char) ch);/* 将Printf内容发往串口 */
  162.         while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); /* 等待发送结束 */
  163.         return (ch);
  164. }  
  165. /**************************************************************
  166. ** 函数名 :fgetc
  167. ** 功能   :重定向c库函数scanf到USART
  168. ** 注意   :由scanf调用
  169. ***************************************************************/
  170. int fgetc(FILE *f)//模拟
  171. {       
  172.         while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);// 等待串口3输入数据
  173.         return (int)USART_ReceiveData(USART1);  
  174. }

  175. /*********************************************END OF FILE**********************/
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2022-7-19 16:55:48 | 显示全部楼层
学习了,不知道用啥好,新手

出0入0汤圆

发表于 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
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-16 05:22

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表