AURIX™ TC397 MCMCAN解析demo(勘误)

项目场景:

学习CAN总线相关知识是每个汽电行业从业者的必修课之一,使用英飞凌TC397芯片作为MCU进行通讯中,首先进行CAN收发和解析实验,在自己的调试和学习中出现了一些问题,在此记录,望各位兄台少走弯路:

  • 此Demo初期是用英飞凌示例程序中的两节点通过环回模式通讯,当发送和接收到数据后点亮LED
    具体可参考:MCMCAN_1_KIT_TC397_TFT
  • 第二部分为了实现CAN收发功能,并在中断中解析报文,参考weifengdq的MCMCAN例程
  • 当我将大佬代码放到自己的板子上运行的时候出现了问题…

问题描述

*英飞凌MCMCAN模块和上位机无法通讯*
通过 AURIX Development Studio(当然HighTec也可以)打印关键的IR寄存器:
IR错误寄存器

可以看到BO位置1,总线关闭状态,说明CAN总线发生了错误。

继续查看ECR寄存器确定错误类型:
在这里插入图片描述

由寄存器说明可知,TEC和CEL置位,说明CAN发送出现主动错误。说明链路没有问题,是程序和板子本身的问题。

具体寄存器的详细描述我摘下来芯片手册,可以看一下

  • IR0
    在这里插入图片描述

  • ERC0
    在这里插入图片描述


原因分析:

问题分析如下:
1.数据链路的问题 CAN节点–CAN线–CAN卡–上位机(通过寄存器排除)
2.板载的CAN控制器问题(环回模式正常排除)
3.最后就是代码问题了,请看下面

CAN初始化部分

void initCAN0(void)
{
    /*******CAN module configuration and initialization*******/
    IfxCan_Can_initModuleConfig(&g_mcmcan.canConfig, &MODULE_CAN0);
    IfxCan_Can_initModule(&g_mcmcan.canModule, &g_mcmcan.canConfig);
    /*******CAN00 node configuration and initialization*******/
    IfxCan_Can_initNodeConfig(&g_mcmcan.canNodeConfig, &g_mcmcan.canModule);
    g_mcmcan.canNodeConfig.nodeId = IfxCan_NodeId_0;
    g_mcmcan.canNodeConfig.clockSource = IfxCan_ClockSource_both;
    g_mcmcan.canNodeConfig.frame.type = IfxCan_FrameType_transmitAndReceive;
    g_mcmcan.canNodeConfig.frame.mode = IfxCan_FrameMode_standard;  //Classic CAN

    g_mcmcan.canNodeConfig.txConfig.txMode = IfxCan_TxMode_dedicatedBuffers;
    g_mcmcan.canNodeConfig.txConfig.dedicatedTxBuffersNumber = 255;
    g_mcmcan.canNodeConfig.txConfig.txBufferDataFieldSize = IfxCan_DataFieldSize_8;

    g_mcmcan.canNodeConfig.rxConfig.rxMode = IfxCan_RxMode_dedicatedBuffers;
    g_mcmcan.canNodeConfig.rxConfig.rxBufferDataFieldSize = IfxCan_DataFieldSize_8;

    g_mcmcan.canNodeConfig.filterConfig.extendedListSize = 255; //Extended Frame
    g_mcmcan.canNodeConfig.filterConfig.messageIdLength = IfxCan_MessageIdLength_extended;
    g_mcmcan.canNodeConfig.baudRate.baudrate = 500000;   //500KBaud
    //transmit interrupt
    g_mcmcan.canNodeConfig.interruptConfig.transmissionCompletedEnabled = TRUE;
    g_mcmcan.canNodeConfig.interruptConfig.traco.priority = ISR_PRIORITY_CAN_TX;
    g_mcmcan.canNodeConfig.interruptConfig.traco.interruptLine = IfxCan_InterruptLine_0;
    g_mcmcan.canNodeConfig.interruptConfig.traco.typeOfService = IfxSrc_Tos_cpu0;
    //receive interrupt
    g_mcmcan.canNodeConfig.interruptConfig.messageStoredToDedicatedRxBufferEnabled = TRUE;
    g_mcmcan.canNodeConfig.interruptConfig.reint.priority = ISR_PRIORITY_CAN_RX;
    g_mcmcan.canNodeConfig.interruptConfig.reint.interruptLine = IfxCan_InterruptLine_1;
    g_mcmcan.canNodeConfig.interruptConfig.reint.typeOfService = IfxSrc_Tos_cpu0;
    //binding pin
    IFX_CONST IfxCan_Can_Pins Can00_pins = 
    {
       &IfxCan_TXD00_P20_8_OUT,   IfxPort_OutputMode_pushPull, // CAN00_TX
       &IfxCan_RXD00B_P20_7_IN,   IfxPort_InputMode_pullUp,    // CAN00_RX
       IfxPort_PadDriver_cmosAutomotiveSpeed4
    };
    g_mcmcan.canNodeConfig.pins = &can00_pins;
    IfxCan_Can_initNode(&g_mcmcan.can00Node, &g_mcmcan.canNodeConfig);
    /*******CAN filter configuration and initialization*******/
    g_mcmcan.canFilter.number = 0;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID1;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);
    g_mcmcan.canFilter.number = 1;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID2;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);
    g_mcmcan.canFilter.number = 2;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID3;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);

}

发送函数部分

void initMsg(void)
{
    IfxCan_Can_initMessage(&g_mcmcan.txMsg);
    g_mcmcan.txMsg.messageId = CAN_MESSAGE_TX_ID0;
    g_mcmcan.txMsg.bufferNumber = 0;
    g_mcmcan.txMsg.dataLengthCode = IfxCan_DataLengthCode_8;//8 bytes
    g_mcmcan.txMsg.frameMode = IfxCan_FrameMode_standard;   //Classic CAN
    g_mcmcan.txMsg.messageIdLength=IfxCan_MessageIdLength_extended;  //Extended Frame
    g_mcmcan.txData[0] = g_mcmcan.rxData[0];
    g_mcmcan.txData[1] = g_mcmcan.rxData[1];
    while(IfxCan_Status_notSentBusy ==
            IfxCan_Can_sendMessage(&g_mcmcan.can02Node, &g_mcmcan.txMsg, &g_mcmcan.txData[0]) )
    {
    }
}

看起来一切正常,环回模式中用的CAN0,CAN1实现收发,怎么把环回打开就不能用了,我百思不得其解。继续阅读芯片手册,改代码,求助大佬,翻论坛,无果。

直到我打开了在文件夹里尘封已久的TC397 demo板手册…


解决方案:

在Demo板3.10节MultiCAN中写道:

On the board is one CAN transceiver (CAN FD capable) connected to the CAN module 0 node 0 (P20.7 and P20.8)
on TC3X7. Optional the CAN transceiver can be connected to CAN module 0 node 2 (P10.2 and P10.3). To do this
remove resistor R269, R270 and assemble R257, R258 with 0R Resistor. For location of this resistors please see the
Top Layer assembling Figure 7-5.
Note: If the board is assembled with TC397 and TLF35584QVVS2 (3,3V version) then the SDMMC module is
connected directly to the SD card. In this case P20.7 and P20.8 are used by the SDMMC and the
transceiver is connected to P10.2 and P10.3.
The transceiver is connected to an IDC10 plug. For the pinout of IDC10 plug see Figure 6-4. You can use a IDC
female connector with crimpconnector, flat cable and SUB-D 9 plug with crimpconnector to have a 1:1 adapter
to SUB-D 9.

大概意思就是:在打板时将SD卡的SDMMC 使用 P20.7 和 P20.8 而CAN收发器连接到 P10.2 和 P10.3,因此要使用CAN2节点和P10.2 和,P10.3口。

太坑爹了!!!

知道问题后修改代码(CPU0全部代码附上):

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "Ifx_Types.h"
#include "IfxCan_Can.h"
#include "IfxCan.h"
#include "IfxCpu_Irq.h"
#include "IfxPort.h"
#include "Bsp.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include "IfxStm.h"
#include "IfxAsclin_Asc.h"

//CPU同步
IFX_ALIGN(4) IfxCpu_syncEvent g_cpuSyncEvent = 0;

/******************************************LED define***********************************************************************/
#define LED0                     &MODULE_P13,0
#define LED1                     &MODULE_P13,1
#define LED2                     &MODULE_P13,2
#define LED3                     &MODULE_P13,3
/******************************************CAN define*************************************************************************/
#define MODULE_CAN0_RAM    0xF0200000
#define NODE0_RAM_OFFSET   0x0

#define CAN_MESSAGE_TX_ID0             (uint32)0xC6
#define CAN_MESSAGE_RX_ID1             (uint32)0xb4
#define CAN_MESSAGE_RX_ID2             (uint32)0x8C
#define CAN_MESSAGE_RX_ID3             (uint32)0x1A3

#define ISR_PRIORITY_CAN_TX         2                       /* Define the CAN TX interrupt priority                 */
#define ISR_PRIORITY_CAN_RX         1                       /* Define the CAN RX interrupt priority                 */
#define MAXIMUM_CAN_DATA_PAYLOAD    2                       /* Define maximum classical CAN payload in 4-byte words */

#define WAIT_TIME                1000


typedef struct
{
    IfxCan_Can_Config canConfig;                            /* CAN module configuration structure                   */
    IfxCan_Can canModule;                                   /* CAN module handle                                    */
    IfxCan_Can_Node can02Node;                             /* CAN source node handle data structure                */
    IfxCan_Can_NodeConfig canNodeConfig;                    /* CAN node configuration structure                     */
    IfxCan_Filter canFilter;                                /* CAN filter configuration structure                   */
    IfxCan_Message txMsg;                                   /* Transmitted CAN message structure                    */
    IfxCan_Message rxMsg;                                   /* Received CAN message structure                       */
    uint32 txData[MAXIMUM_CAN_DATA_PAYLOAD];                /* Transmitted CAN data array                           */
    uint32 rxData[MAXIMUM_CAN_DATA_PAYLOAD];                /* Received CAN data array                              */
} McmcanType;

McmcanType                  g_mcmcan;

/********************************************CAN Implement*************************************************************************************/
void initMsg(void);

IFX_INTERRUPT(canIsrTxHandler, 0, ISR_PRIORITY_CAN_TX);
void canIsrTxHandler(void)
{
    IfxCan_Node_clearInterruptFlag(g_mcmcan.can02Node.node, IfxCan_Interrupt_transmissionCompleted);
    IfxPort_togglePin(LED0);   
}

IFX_INTERRUPT(canIsrRxHandler, 0, ISR_PRIORITY_CAN_RX);
void canIsrRxHandler(void)
{
    /* Clear the "Message stored to Dedicated RX Buffer" interrupt flag */
    IfxCan_Node_clearInterruptFlag(g_mcmcan.can02Node.node, IfxCan_Interrupt_messageStoredToDedicatedRxBuffer);
    /* Clear the "New Data" flag; as long as the "New Data" flag is set, the respective Rx buffer is
     * locked against updates from received matching frames.
     */
    IfxCan_Node_clearRxBufferNewDataFlag(g_mcmcan.can02Node.node, g_mcmcan.canFilter.rxBufferOffset);
    /* Read the received CAN message */
    IfxCan_Can_readMessage(&g_mcmcan.can02Node, &g_mcmcan.rxMsg, g_mcmcan.rxData);

    /* Check if the received data matches with the transmitted one */
    if(g_mcmcan.rxMsg.messageId == CAN_MESSAGE_RX_ID1)
    {
            IfxPort_setPinState(LED1, IfxPort_State_low);   //LED ON

            uint8 Checksum = (g_mcmcan.rxData[1] >>24) & 0xFF;
            uint8 WheelSpeed_FR = (g_mcmcan.rxData[1] >> 16) & 0xFF;
            uint8 WheelSpeed_FL = (g_mcmcan.rxData[1] >> 8) & 0xFF;
            uint8 ABS_VehicleSpeed = g_mcmcan.rxData[1] & 0xFF;
            uint8 LiveConter = (g_mcmcan.rxData[0]>>24) & 0xFF ;
            uint8 ABS_DrivingDirection = (g_mcmcan.rxData[0] >> 16) & 0xFF;
            uint32 speedKmPerHour = ABS_VehicleSpeed;
            float speedMps = (float)speedKmPerHour * 1000.0 / 3600.0;

            printf("*********************************************************\n");
            printf("ABS_VehicleSpeed: %.2f m/s\n", speedMps);
            printf("WheelSpeed_FL: %u kmh\n", WheelSpeed_FL);
            printf("WheelSpeed_FR: %u kmh\n", WheelSpeed_FR);
            switch(ABS_DrivingDirection)
            {
                case 0 :  printf("front\n");
                    break;
                case 1 :  printf("back\n");
                    break;
                case 2 :  printf("left\n");
                    break;
                case 3 :  printf("right\n");
                    break;
            }
            printf("Checksum: %u\n", Checksum);
            printf("LiveConter: %u\n", LiveConter);
            printf("*********************************************************\n");
        }
        else
        {
            IfxPort_setPinState(LED1, IfxPort_State_high);  //LED OFF
        }
    if(g_mcmcan.rxMsg.messageId == CAN_MESSAGE_RX_ID2)
    {
        initMsg();
    }
    else
    {
        IfxPort_setPinState(LED2, IfxPort_State_high);
    }
    if(g_mcmcan.rxMsg.messageId == CAN_MESSAGE_RX_ID3)
    {
       IfxPort_setPinState(LED3, IfxPort_State_low);
    }
    else
    {
       IfxPort_setPinState(LED3, IfxPort_State_high);
    }
}

void initCAN0(void)
{
    /*******CAN module configuration and initialization*******/
    IfxCan_Can_initModuleConfig(&g_mcmcan.canConfig, &MODULE_CAN0);
    IfxCan_Can_initModule(&g_mcmcan.canModule, &g_mcmcan.canConfig);

    /*******CAN00 node configuration and initialization*******/
    IfxCan_Can_initNodeConfig(&g_mcmcan.canNodeConfig, &g_mcmcan.canModule);
    g_mcmcan.canNodeConfig.nodeId = IfxCan_NodeId_2;
    g_mcmcan.canNodeConfig.clockSource = IfxCan_ClockSource_both;
    g_mcmcan.canNodeConfig.frame.type = IfxCan_FrameType_transmitAndReceive;
    g_mcmcan.canNodeConfig.frame.mode = IfxCan_FrameMode_standard;  //Classic CAN
    g_mcmcan.canNodeConfig.txConfig.txMode = IfxCan_TxMode_dedicatedBuffers;
    g_mcmcan.canNodeConfig.txConfig.dedicatedTxBuffersNumber = 255;
    g_mcmcan.canNodeConfig.txConfig.txBufferDataFieldSize = IfxCan_DataFieldSize_8;
    g_mcmcan.canNodeConfig.rxConfig.rxMode = IfxCan_RxMode_dedicatedBuffers;
    g_mcmcan.canNodeConfig.rxConfig.rxBufferDataFieldSize = IfxCan_DataFieldSize_8;
    g_mcmcan.canNodeConfig.filterConfig.extendedListSize = 255; //Extended Frame
    g_mcmcan.canNodeConfig.filterConfig.messageIdLength = IfxCan_MessageIdLength_extended;
    g_mcmcan.canNodeConfig.messageRAM.extendedFilterListStartAddress = 0x100;   //Extended Frame
    g_mcmcan.canNodeConfig.messageRAM.rxBuffersStartAddress = 0x200;
    g_mcmcan.canNodeConfig.messageRAM.txBuffersStartAddress = 0x400;
    g_mcmcan.canNodeConfig.messageRAM.baseAddress = MODULE_CAN0_RAM + NODE0_RAM_OFFSET;
    g_mcmcan.canNodeConfig.baudRate.baudrate = 500000;   //500KBaud
    //transmit interrupt
    g_mcmcan.canNodeConfig.interruptConfig.transmissionCompletedEnabled = TRUE;
    g_mcmcan.canNodeConfig.interruptConfig.traco.priority = ISR_PRIORITY_CAN_TX;
    g_mcmcan.canNodeConfig.interruptConfig.traco.interruptLine = IfxCan_InterruptLine_0;
    g_mcmcan.canNodeConfig.interruptConfig.traco.typeOfService = IfxSrc_Tos_cpu0;
    //receive interrupt
    g_mcmcan.canNodeConfig.interruptConfig.messageStoredToDedicatedRxBufferEnabled = TRUE;
    g_mcmcan.canNodeConfig.interruptConfig.reint.priority = ISR_PRIORITY_CAN_RX;
    g_mcmcan.canNodeConfig.interruptConfig.reint.interruptLine = IfxCan_InterruptLine_1;
    g_mcmcan.canNodeConfig.interruptConfig.reint.typeOfService = IfxSrc_Tos_cpu0;
    //binding pin
    IFX_CONST IfxCan_Can_Pins can02_pins = {
           &IfxCan_TXD02_P10_3_OUT,   IfxPort_OutputMode_pushPull,
           &IfxCan_RXD02E_P10_2_IN,   IfxPort_InputMode_pullUp,
           IfxPort_PadDriver_cmosAutomotiveSpeed4
    };
    g_mcmcan.canNodeConfig.pins = &can02_pins;
    IfxCan_Can_initNode(&g_mcmcan.can02Node, &g_mcmcan.canNodeConfig);
    /*******CAN filter configuration and initialization*******/
    g_mcmcan.canFilter.number = 0;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID1;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);
    g_mcmcan.canFilter.number = 1;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID2;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);
    g_mcmcan.canFilter.number = 2;
    g_mcmcan.canFilter.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer;
    g_mcmcan.canFilter.id1 = CAN_MESSAGE_RX_ID3;
    g_mcmcan.canFilter.rxBufferOffset = IfxCan_RxBufferId_0;
    IfxCan_Can_setExtendedFilter(&g_mcmcan.can02Node, &g_mcmcan.canFilter);
}
void initLED(void)
{
    IfxPort_setPinMode(LED0, IfxPort_Mode_outputPushPullGeneral);    /* Initialize LED port pin                      */
    IfxPort_setPinMode(LED1, IfxPort_Mode_outputPushPullGeneral);
    IfxPort_setPinMode(LED2, IfxPort_Mode_outputPushPullGeneral);
    IfxPort_setPinMode(LED3, IfxPort_Mode_outputPushPullGeneral);
    IfxPort_setPinState(LED0, IfxPort_State_high);                   /* Turn off LED (LED is low-level active)       */
    IfxPort_setPinState(LED1, IfxPort_State_high);
    IfxPort_setPinState(LED2, IfxPort_State_high);
    IfxPort_setPinState(LED3, IfxPort_State_high);
}
void initMsg(void)
{
    IfxCan_Can_initMessage(&g_mcmcan.txMsg);
    g_mcmcan.txMsg.messageId = CAN_MESSAGE_TX_ID0;
    g_mcmcan.txMsg.bufferNumber = 0;
    g_mcmcan.txMsg.dataLengthCode = IfxCan_DataLengthCode_8;//8 bytes
    g_mcmcan.txMsg.frameMode = IfxCan_FrameMode_standard;   //Classic CAN
    g_mcmcan.txMsg.messageIdLength=IfxCan_MessageIdLength_extended;  //Extended Frame
    g_mcmcan.txData[0] = g_mcmcan.rxData[0];
    g_mcmcan.txData[1] = g_mcmcan.rxData[1];
    while(IfxCan_Status_notSentBusy ==
            IfxCan_Can_sendMessage(&g_mcmcan.can02Node, &g_mcmcan.txMsg, &g_mcmcan.txData[0]) )
    {
    }
}

void core0_main(void)
{
    IfxCpu_enableInterrupts();
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
    initLED();
    initCAN0();

    while(1)
    {
        waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, WAIT_TIME));
    }
}

代码编译运行正常,接上CAN卡发送数据

配置ID为0xb4的帧做报文解析,ID为0x8c直接转发,ID为0x1a3点灯
在这里插入图片描述

可以看到五帧报文全部发送成功!
在这里插入图片描述

AURIX Development Studio 模拟串口print如下:
在这里插入图片描述

到此为止,MCMCAN收发并解析问题解决,我也顺利完成了师父交给我的学习任务,加油汽电人!
在这里插入图片描述

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: 《Java编程思想》是一本经典的Java编程教材,由Bruce Eckel撰写。勘误是指对书中错误的修正或补充。 首先,需要注意的是这本书非常全面,覆盖了Java语言的方方面面。然而,由于计算机技术的快速发展,书中可能存在一些错误或遗漏。 勘误是为了确保读者获得正确的信息和指导,因此,许多作者或出版商会提供书籍的勘误表。读者可以通过查看这些勘误表来了解书中可能存在的错误,并及时进行校正。 在《Java编程思想》这本书的勘误中,可能会包含一些代码错误、文字错误、图表错误等。这些错误会被列出,并附上正确的版本。 同时,勘误也可能会补充一些遗漏的内容,以完善书籍的内容。这些内容可能包括新的Java语言特性、最新的开发工具或最佳实践等。 读者可以通过查找《Java编程思想》的勘误表,来获取修正后的版本,以确保在学习和实践过程中不会受到错误信息的影响。 总之,勘误是修正《Java编程思想》中可能存在的错误或遗漏的过程。通过查找勘误表,读者可以获得更准确和全面的信息,提高学习效果和编程水平。 ### 回答2: 《Java编程思想》是一本关于Java编程的经典著作,由美国作家Bruce Eckel撰写。本书内容深入浅出,系统地介绍了Java编程的各个方面,并包含了大量的实例和示例代码帮助读者理解和掌握相关知识。 关于《Java编程思想》的翻译勘误,根据我所了解,尽管本书经过了不少次修订和校对,但仍然难免存在一些错误或疏漏。这些错误可能包括错别字、语法问题、排版错误等。 如果读者在阅读这本书的过程中发现了错误,可以通过与出版社联系,向他们报告错误并提供正确的信息。出版社会在下一版中进行勘误修正,以确保读者能够得到更准确、更完整的内容。 对于一本经典的图书来说,勘误是常见的,而且也是作者和出版社重视书籍质量的表现。读者可以通过关注官方网站或者社区论坛等渠道获取最新的勘误信息,以确保自己所持有的版本是最正确的。 总之,《Java编程思想》是一本非常有价值的Java编程指南,对于学习和掌握Java编程语言以及面向对象编程思想的读者来说都是不可或缺的学习资料。尽管存在一些可能的勘误,但读者可以通过与出版社联系来帮助改进和完善这本书的质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炸弹气旋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值