This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[求助]发现2.1.0.12573版本Tiva外设库存在错误(使用的是官方\examples\peripherals提供的代码,而改成ROM中的函数则无问题)

Other Parts Discussed in Thread: EK-TM4C123GXL

在使用官方的代码的时候,发现程序运行起来后就会复位。很是奇怪,然而发现之前应用的一段类似的代码却没有问题,一步步缩小错误范围之后发现,唯一的区别就是使用ROM中的函数程序运行正常,而使用里面的函数则程序会自动复位

最后定位到UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);这一个函数上。

使用UARTClockSourceSet程序会自动复位,而是用ROM_UARTClockSourceSet程序运转正常。

就是这个简单的命令行初始化函数。
void InitConsole(void)
{
    //
    // Enable GPIO port A which is used for UART0 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    //
    // Enable UART0 so that we can configure the clock.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}

这是官方提供的代码段,未做修改。

我在KEIL5中用Jlink仿真的时候,全速运行,程序后自动复位。

然后如果将断点设置在

UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
一句上,再向下运行,程序则又可以运行。

于是我就怀疑是不是外设还没有使能,这函数才出错的,我就屏蔽掉
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
这一句外设使能。发现设置断点程序也会复位

这时我就考虑在
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
前面加一句延时函数
SysCtlDelay(100000);
这时全速运行程序就没有错误了。

然后如果将官方代码
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
改为
ROM_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
程序不需要延时也可以运行。


最后总结:
感觉应该是
UARTClockSourceSet函数中缺少判断外设是否使能的操作,然后外设还没来得及初始化,就操作那个寄存器,这样才导致系统复位的。

不知道理解是否有误,因为比赛临近,没时间细致研究库函数的寄存器操作是否有误了。
或者可能是我的忽略了哪个地方?库函数根本没有错误,望工程师们能帮我做个测试。帮我解决这个疑惑,谢了。。。。

上传我会出错的代码。代码完全是DK-TM4C123G Firmware Development Package\examples\peripherals\systick下的文件,未做修改。
库函数版本 2.1.0.12573 开发环境是Keil 5.1 芯片TM4C213GH6PGEI 仿真器是Jlink V8
  • 楼主试一下这个例程:

    C:\ti\TivaWare_C_Series-2.1.0.12573\examples\boards\ek-tm4c123gxl\hello

    这里也用了UARTClockSourceSet函数。我跑了一下没问题的,我也用的Keil 5.1

    另,请把你建立的完整工程传上来一起看一下。

    有时候,故障现象背后可能会有更深入的原因。

  • 孙工,以前官方的F232 USB+CAN的板子用的是哪个OLED呢,我的那个坏了,想买个配上去了

  • 感谢你的耐心解答:

    你说的这个工程确实又没有问题,对比了一下,好像就只有寄存器的配置顺序不一样,再就是我使用的是systick,就这些区别,实在看不出哪儿有问题。

    可不可以再麻烦你帮我看看代码,很简短的。看看我问题到底出在哪儿?

    我的完整的工程代码已经上传了,就是文中那个蓝色链接,不知道为什么没有附件显示。点击就可以下载。

  • 型号为:

    CFAL9664B-F-B1

  • 楼主,我看了一下你的工程。

    请你先验证一下把SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);这句话放到SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);之后,调整一下这个顺序,然后再试试问题是否解决。

    一般情况下,使能一个外设后,需要5个时钟周期,才可以对这个外设进行操作。例如如果我们用了A B 两个外设,好的习惯是这样做:

    使能A;

    使能B;

    配置A;

    配置B;

    调用ROM可以解决的原因是,程序调用也需要时钟周期,调用ROM到真正操作寄存器的时间大于5个时钟周期,所以OK了。另外你加延迟,也是这个道理。大于5个时钟周期就没问题。

    你也可以看一下库函数的源码,注释有描述:

    //*****************************************************************************
    //
    //! Enables a peripheral.
    //!
    //! \param ui32Peripheral is the peripheral to enable.
    //!
    //! This function enables a peripheral. At power-up, all peripherals are
    //! disabled; they must be enabled in order to operate or respond to register
    //! reads/writes.
    //!
    //! The \e ui32Peripheral parameter must be only one of the following values:
    //! \b SYSCTL_PERIPH_ADC0, \b SYSCTL_PERIPH_ADC1, \b SYSCTL_PERIPH_CAN0,
    //! \b SYSCTL_PERIPH_CAN1, \b SYSCTL_PERIPH_CCM0,\b SYSCTL_PERIPH_COMP0,
    //! \b SYSCTL_PERIPH_EEPROM0, \b SYSCTL_PERIPH_EMAC, \b SYSCTL_PERIPH_EPHY,
    //! \b SYSCTL_PERIPH_EPI0,
    //! \b SYSCTL_PERIPH_GPIOA, \b SYSCTL_PERIPH_GPIOB, \b SYSCTL_PERIPH_GPIOC,
    //! \b SYSCTL_PERIPH_GPIOD, \b SYSCTL_PERIPH_GPIOE, \b SYSCTL_PERIPH_GPIOF,
    //! \b SYSCTL_PERIPH_GPIOG, \b SYSCTL_PERIPH_GPIOH, \b SYSCTL_PERIPH_GPIOJ,
    //! \b SYSCTL_PERIPH_GPIOK, \b SYSCTL_PERIPH_GPIOL, \b SYSCTL_PERIPH_GPIOM,
    //! \b SYSCTL_PERIPH_GPION, \b SYSCTL_PERIPH_GPIOP, \b SYSCTL_PERIPH_GPIOQ,
    //! \b SYSCTL_PERIPH_GPIOR, \b SYSCTL_PERIPH_GPIOS, \b SYSCTL_PERIPH_GPIOT,
    //! \b SYSCTL_PERIPH_HIBERNATE,
    //! \b SYSCTL_PERIPH_I2C0, \b SYSCTL_PERIPH_I2C1, \b SYSCTL_PERIPH_I2C2,
    //! \b SYSCTL_PERIPH_I2C3, \b SYSCTL_PERIPH_I2C4, \b SYSCTL_PERIPH_I2C5,
    //! \b SYSCTL_PERIPH_I2C6, \b SYSCTL_PERIPH_I2C7, \b SYSCTL_PERIPH_I2C8,
    //! \b SYSCTL_PERIPH_I2C9, \b SYSCTL_PERIPH_LCD0,
    //! \b SYSCTL_PERIPH_ONEWIRE0,
    //! \b SYSCTL_PERIPH_PWM0, \b SYSCTL_PERIPH_PWM1, \b SYSCTL_PERIPH_QEI0,
    //! \b SYSCTL_PERIPH_QEI1, \b SYSCTL_PERIPH_SSI0, \b SYSCTL_PERIPH_SSI1,
    //! \b SYSCTL_PERIPH_SSI2, \b SYSCTL_PERIPH_SSI3, \b SYSCTL_PERIPH_TIMER0,
    //! \b SYSCTL_PERIPH_TIMER1, \b SYSCTL_PERIPH_TIMER2, \b SYSCTL_PERIPH_TIMER3,
    //! \b SYSCTL_PERIPH_TIMER4, \b SYSCTL_PERIPH_TIMER5, \b SYSCTL_PERIPH_TIMER6,
    //! \b SYSCTL_PERIPH_TIMER7, \b SYSCTL_PERIPH_UART0, \b SYSCTL_PERIPH_UART1,
    //! \b SYSCTL_PERIPH_UART2, \b SYSCTL_PERIPH_UART3, \b SYSCTL_PERIPH_UART4,
    //! \b SYSCTL_PERIPH_UART5, \b SYSCTL_PERIPH_UART6, \b SYSCTL_PERIPH_UART7,
    //! \b SYSCTL_PERIPH_UDMA, \b SYSCTL_PERIPH_USB0, \b SYSCTL_PERIPH_WDOG0,
    //! \b SYSCTL_PERIPH_WDOG1, \b SYSCTL_PERIPH_WTIMER0, \b SYSCTL_PERIPH_WTIMER1,
    //! \b SYSCTL_PERIPH_WTIMER2, \b SYSCTL_PERIPH_WTIMER3,
    //! \b SYSCTL_PERIPH_WTIMER4, or \b SYSCTL_PERIPH_WTIMER5
    //!
    //! \note It takes five clock cycles after the write to enable a peripheral
    //! before the the peripheral is actually enabled. During this time, attempts
    //! to access the peripheral result in a bus fault. Care should be taken
    //! to ensure that the peripheral is not accessed during this brief time
    //! period.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void
    SysCtlPeripheralEnable(uint32_t ui32Peripheral)
    {
    //
    // Check the arguments.
    //
    ASSERT(_SysCtlPeripheralValid(ui32Peripheral));

    //
    // Enable this peripheral.
    //
    HWREGBITW(SYSCTL_RCGCBASE + ((ui32Peripheral & 0xff00) >> 8),
    ui32Peripheral & 0xff) = 1;
    }

  • 这个细节真的很容易忽略,但是以前玩别的芯片基本不会出现这种情况,是不是可以这么说,库中为了效率,把这些判断的语句省略了

  • 非常感谢您的耐心解答,困惑终于解决了。以前也用过几种单片机,TI的MSP430系列单片机也用过很长一段时间。从来没遇到过这个问题,可能因为效率的问题库函数里面才不会加判断外设是否使能的语句吧?

    因为这个问题,而外设使能所需时间也并非固定的,有某些程序经常这样偶尔可以工作偶尔不可以!!!而又无法查出程序的错误,曾一度怀疑单片机的稳定性。

    顺便提一点意见:

    1.我这个程序是直接复制官方提供的demo,未做修改。希望可以反馈并更正一下。

    2.我觉得这个提示还是挺重要的,不应该放在这个不起眼的地方,一般人很容易就忽略了。

    这两个问题叠在一起之后就很难调试发现错误。幸亏有你的解答。再次感谢。

    3.还有官方提供的MPU6050的驱动,文档中示例的初始化也无法使用。后来发现是因为初始化中复位了传感器,然后传感器休眠了,所以需要在初始化之后加一句唤醒MPU6050的操作。代码才可以正常工作。

    --------------------------还有一些问题请教,麻烦了-----------

    1按您的说法,是不是使用ROM中的库还慢于直接使用库函数??虽然可以节省代码空间,但是为了效率,使用库函数是不是更好?还是说这个效率问题可以忽略?

    2芯片的版本,TARGET_IS_TM4C123_???可以随便选取吗?我发现版本不一样,ROM中的库函数也会有一定的区别。如果选择错误是不是程序也会异常。需不需要先读取寄存器获得相应的版本号后再使用?或者说可以从芯片外部标志获得版本号???

  • 1. 我们会反馈这个问题给产品线,在下一版进行修复。

    2.实际上ROM中代码的执行会比Flash稍微快一点。这里实际上是因为多跳转了几下,所以时间超过了5个周期。一般情况下,初始化的代码无所谓的,快点慢点都ok。在中断服务程序中可以考虑放一些效率高的代码,要求更苛刻就直接操作寄存器。

    3. TARGET_IS_TM4C123_这个要和自己芯片的版本来对照才行。可以直接通过IDE读取DID0寄存器来看版本号。寄存器值和版本号对应关系,在芯片的勘误表文档中,开始位置有个表可以参考。

    4. 老的芯片外部丝印不带芯片版本,应该是从B2版本吧,后面带版本号了。这个可以看最新的Datasheet中对命名的描述。

  • 以前从不关心勘误表,下次会注意的。

    发现datasheet中的芯片标志格式和我的芯片不一样。后来通过读寄存器的方式获得了版本号,为B1。

    再次感谢您的耐心解答。

    贴一段读DID0寄存器的代码,虽然很简单,但是可以给有需要的朋友节省点时间。

    #include <stdint.h>
    #include "inc/tm4c123gh6pge.h"
    uint32_t ui32DIDO;
    uint8_t reserved_31,VER,reserved_24_27,CLASS,MAJOR,MINOR;
    int main(void)
    {
    	ui32DIDO = SYSCTL_DID0_R;
    	MINOR = SYSCTL_DID0_R&0x000000ff;
    	MAJOR = (SYSCTL_DID0_R&0x0000ff00)>>8;
    	CLASS = (SYSCTL_DID0_R&0x00ff0000)>>16;
    	reserved_24_27 = (SYSCTL_DID0_R&0x0f000000)>>24;
    	VER = (SYSCTL_DID0_R&0x70000000)>>28;
    	reserved_31 = (SYSCTL_DID0_R&0x80000000)>>31;
    	while(1){}
    }

    读到的MAJOR , MINOR的值对应版本号。 
  • 感谢楼主的分享!

    做产品设计时建议先浏览勘误表。所有的芯片都会或多或少有一些小问题,这个也挺正常的。

    勘误表都会提供解决方法,前期硬件设计时充分考虑,可以大幅缩短调试时间。

  • 在请教您一个问题啊!看了一天的定时器模块,还是没弄懂寄存器的整个描述。

    使用的芯片是TM4C123G

    1.数据手册中描述的Prescale,到底是不是分频器,我看系统框图上并没有画分频器。而且正好所有关于Prescaler寄存器的描述都很不像分频器,特别是Register 16: GPTM TimerA Prescale Match (GPTMTAPMR)这个寄存器,说是为了扩展匹配值的范围??还是说这芯片不支持分频。只能使用和系统同样的频率。

    2.我在使用Input Edge-Time Mode模式测量脉宽的时候,设置了TimerLoadSet(TIMER4_BASE, TIMER_A, 0x0fff);并且是增计数。这个我们知道是说计数从0开始,计到0xfff,又会从新从0开始计数。后来我又添加了    TimerPrescaleSet(TIMER4_BASE, TIMER_A, 200);配置,起初移植理解为分频值。但是后来调试发现计数值会大于0x0fff,我就很奇怪了。如果将TimerPrescaleSet理解为扩展计数器。将设置的值扩展16~23位。但是为什么TimerLoadSet设置的时间阈值也会改变?那这个时间阈值到底是多少呢?

    由于代码没有错误,只是不理解,所以就不上传代码了。。。