关于在keil中进行stm32软件stm32 keil 仿真波形的问题

STM32调试过程中常见的问题及解决方法
STM32调试过程中常见的问题及解决方法
一、 在“Debug选项卡”下设置好仿真器的类型后,下载程序时却提示“No ULINK Device found.”
&& 解决办法: Keil MDK默认使用ULINK仿真器下载程序,在“Utilities选项卡”下把编程所使用的仿真器改为相应的类型即可。
二、 编译工程时提示如下信息:
main.axf: Error: L6218E: Undefined symbol __BASEPRICONFIG (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __GetBASEPRI (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __RESETFAULTMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __RESETPRIMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __SETFAULTMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __SETPRIMASK (referred from stm32f10x_nvic.o).
&&& 解决办法:工程缺少“cortexm3_macro.s”文件,把cortexm3_macro.s和STM3210x.s全部添加到工程即可。
三、调试器不能连接到STM32的问题与解决办法
&&& 很多人都碰到过调试器不能连接到STM32的问题,不管是IAR的J-Link还是Keil的ULink,或者是ST的ST-Link。出现这个问题时,调试软件会提示不能建立与Cortex-M3的连接,或提示不能下载程序,或提示找不到要调试的设备等。
&&& 这样的问题都是发生在调试那些可以在CPU不干预的时候自动运行的模块、或在调试低功耗模式的程序的时候。所谓“可以在CPU不干预的时候自动运行的模块”包括:DMA、定时器、连续转换模式下的ADC、看门狗等模块。
--------------------------------------------------------------------------------
这个问题的根源是:
&&& 1. 调试器需要在RAM内执行一段程序,对Flash进行擦写操作,如果不停止这些自动运行的模块,它们会干扰程序在RAM中的执行,致使下载失败。比如DMA模块被配置为不停地拷贝一段数据区,而调试器刚好需要使用DMA数据传输的目标区域,这时DMA的操作将会与调试器的操作发生冲突。再比如,如果启动了看门狗而没有执行硬件复位,则在下次调试器需要下载程序时,看门狗超时将触发芯片复位,导致下载操作失败。
&&& 2. 低功耗是通过停止CPU的时钟而实现,JTAG调试是通过与CPU的通信实现,停止了CPU的时钟致使调试器会失去与CPU的通信。
--------------------------------------------------------------------------------
&&& 有人说“我停止调试的时候,这些模块已经停止了运行,应该不会干扰到后续的调试”,这个问题要从几方面看:
&&& 1. 调试器是通过停止CPU核心的时钟来停止被调试程序的运行,实际上被调试芯片的硬件模块并没有被复位,它们还处于使能状态,那些能够自动运行的模块只是处于暂停状态,一旦恢复了时钟之后,它们仍会继续运行。
&&& 2. 目前常用的调试软件,不管是IAR EWARM还是Keil MDK,调试软件界面上的"复位"按钮都不能对芯片执行硬件的复位,这个"复位"按钮只能对芯片内的程序执行软件复位,即把运行指针重新指向复位地址。
&&& 3. 使用板上的复位按钮可以手动地进行硬件复位,使所有模块(包括那些能够自动运行的模块)停止工作并恢复到复位状态。但是当调试器需要控制CPU之前,它需要先为CPU核心提供时钟,然后需要较长的一段时间做一些初始化的动作,然后才能接管CPU核心的控制权。在调试器为CPU核心提供时钟之后,用户程序就已经开始运行起来,如果用户程序在调试器接管CPU核心的控制权之前,就初始化好硬件模块并启动运行,则仍然会产生与调试器的冲突。
--------------------------------------------------------------------------------
&&& 根据以上的分析,解决这个问题的关键是,在调试器接管CPU核心的控制权之前,必须停止所有能够自动运行模块的操作,使它们处于关闭状态,要做到这一点,可以有以下几种方案:
&&& 1. 每次退出调试状态时,先停止所有模块的运行,比如执行该模块的DeInit()操作。
&&& 2. 在main()函数开始时,不管各模块处于什么状态,先执行该模块的DeInit()操作,然后在程序中较晚的时间或真正需要时再开启相应的模块。这样保证在刚进入调试状态时,调试器能够有充足的时间完成初始化和下载程序的操作。先执行该模块的DeInit()操作的目的是为了关闭哪些上一次操作开启的模块。
&&& 3. 调整BOOT0/BOOT1的设置,把启动模式改变为从内部SRAM启动,再结合手工硬件复位。由于BOOT0/BOOT1的状态只在硬件复位时是有意义的,而调试器不做硬件复位,所以这样的设置不会影响调试器下载程序到Flash中,也不会影响在Flash中调试程序。
四、调试STM32程序时,某些标志位被调试软件意外清除的问题
&&& 在调试的过程中,使用调试软件的寄存器或存储器显示窗口,可以很方便地查看外设寄存器的状态。
&&& 很多朋友都碰到过这样的问题:在单步调试时始终不能在显示窗口看到某些标志位的变化,应该设置这些标志位的时候,窗口中却显示为0,不少人都错误地认为这是芯片的问题。
&&& 我们知道,不少STM32外设的状态寄存器位,可以通过对某些寄存器的读操作而清除(例如I2C的I2C_SR1中的很多标志位),在调试过程中,每当程序停止在设置的断点或单步停止时,调试软件都会自动地读出所有指定的寄存器和存储器中的内容,并刷新窗口的显示,调试软件的这个读操作恰好清除了那些标志位,造成了上面描述的现象。
&&& 有几个简单的办法解决这个问题:
& 1. 关闭寄存器或存储器显示窗口。
& 2. 在寄存器或存储器显示窗口中不显示这些敏感的寄存器。
& 3. 不要把断点放在对这些敏感的寄存器位操作的前面,以保证这些寄存器位不被调试软件意外地操作。
& 4. 看官自己添加~~~~~
五、在使用STM32的外设时,由于IO口被用作复用功能,但是外设的初始化正确,GPIO口初始化正确,外设的时钟也已开启,但是外设无法正常运行
&&& 其中最关键的一项,大多数使用者多没有设置,就是某个IO口被用作外设的接口时,需要开启IO口的复用功能的时钟,即进行外设、IO的时钟使能时,需要如下代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx | RCC_APB2Periph_AFIO, ENABLE);&&& /* GPIOx and AFIO clock enable */
x --- 为对应的GPIO口,如:A、B、C、D、E。
&&& 在使用时,一定要注意该要点!一、 在“Debug选项卡”下设置好仿真器的类型后,下载程序时却提示“No ULINK Device found.”
&& 解决办法: Keil MDK默认使用ULINK仿真器下载程序,在“Utilities选项卡”下把编程所使用的仿真器改为相应的类型即可。
二、 编译工程时提示如下信息:
main.axf: Error: L6218E: Undefined symbol __BASEPRICONFIG (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __GetBASEPRI (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __RESETFAULTMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __RESETPRIMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __SETFAULTMASK (referred from stm32f10x_nvic.o).
main.axf: Error: L6218E: Undefined symbol __SETPRIMASK (referred from stm32f10x_nvic.o).
&&& 解决办法:工程缺少“cortexm3_macro.s”文件,把cortexm3_macro.s和STM3210x.s全部添加到工程即可。
三、调试器不能连接到STM32的问题与解决办法
&&& 很多人都碰到过调试器不能连接到STM32的问题,不管是IAR的J-Link还是Keil的ULink,或者是ST的ST-Link。出现这个问题时,调试软件会提示不能建立与Cortex-M3的连接,或提示不能下载程序,或提示找不到要调试的设备等。
&&& 这样的问题都是发生在调试那些可以在CPU不干预的时候自动运行的模块、或在调试低功耗模式的程序的时候。所谓“可以在CPU不干预的时候自动运行的模块”包括:DMA、定时器、连续转换模式下的ADC、看门狗等模块。
--------------------------------------------------------------------------------
这个问题的根源是:
&&& 1. 调试器需要在RAM内执行一段程序,对Flash进行擦写操作,如果不停止这些自动运行的模块,它们会干扰程序在RAM中的执行,致使下载失败。比如DMA模块被配置为不停地拷贝一段数据区,而调试器刚好需要使用DMA数据传输的目标区域,这时DMA的操作将会与调试器的操作发生冲突。再比如,如果启动了看门狗而没有执行硬件复位,则在下次调试器需要下载程序时,看门狗超时将触发芯片复位,导致下载操作失败。
&&& 2. 低功耗是通过停止CPU的时钟而实现,JTAG调试是通过与CPU的通信实现,停止了CPU的时钟致使调试器会失去与CPU的通信。
--------------------------------------------------------------------------------
&&& 有人说“我停止调试的时候,这些模块已经停止了运行,应该不会干扰到后续的调试”,这个问题要从几方面看:
&&& 1. 调试器是通过停止CPU核心的时钟来停止被调试程序的运行,实际上被调试芯片的硬件模块并没有被复位,它们还处于使能状态,那些能够自动运行的模块只是处于暂停状态,一旦恢复了时钟之后,它们仍会继续运行。
&&& 2. 目前常用的调试软件,不管是IAR EWARM还是Keil MDK,调试软件界面上的"复位"按钮都不能对芯片执行硬件的复位,这个"复位"按钮只能对芯片内的程序执行软件复位,即把运行指针重新指向复位地址。
&&& 3. 使用板上的复位按钮可以手动地进行硬件复位,使所有模块(包括那些能够自动运行的模块)停止工作并恢复到复位状态。但是当调试器需要控制CPU之前,它需要先为CPU核心提供时钟,然后需要较长的一段时间做一些初始化的动作,然后才能接管CPU核心的控制权。在调试器为CPU核心提供时钟之后,用户程序就已经开始运行起来,如果用户程序在调试器接管CPU核心的控制权之前,就初始化好硬件模块并启动运行,则仍然会产生与调试器的冲突。
--------------------------------------------------------------------------------
&&& 根据以上的分析,解决这个问题的关键是,在调试器接管CPU核心的控制权之前,必须停止所有能够自动运行模块的操作,使它们处于关闭状态,要做到这一点,可以有以下几种方案:
&&& 1. 每次退出调试状态时,先停止所有模块的运行,比如执行该模块的DeInit()操作。
&&& 2. 在main()函数开始时,不管各模块处于什么状态,先执行该模块的DeInit()操作,然后在程序中较晚的时间或真正需要时再开启相应的模块。这样保证在刚进入调试状态时,调试器能够有充足的时间完成初始化和下载程序的操作。先执行该模块的DeInit()操作的目的是为了关闭哪些上一次操作开启的模块。
&&& 3. 调整BOOT0/BOOT1的设置,把启动模式改变为从内部SRAM启动,再结合手工硬件复位。由于BOOT0/BOOT1的状态只在硬件复位时是有意义的,而调试器不做硬件复位,所以这样的设置不会影响调试器下载程序到Flash中,也不会影响在Flash中调试程序。
四、调试STM32程序时,某些标志位被调试软件意外清除的问题
&&& 在调试的过程中,使用调试软件的寄存器或存储器显示窗口,可以很方便地查看外设寄存器的状态。
&&& 很多朋友都碰到过这样的问题:在单步调试时始终不能在显示窗口看到某些标志位的变化,应该设置这些标志位的时候,窗口中却显示为0,不少人都错误地认为这是芯片的问题。
&&& 我们知道,不少STM32外设的状态寄存器位,可以通过对某些寄存器的读操作而清除(例如I2C的I2C_SR1中的很多标志位),在调试过程中,每当程序停止在设置的断点或单步停止时,调试软件都会自动地读出所有指定的寄存器和存储器中的内容,并刷新窗口的显示,调试软件的这个读操作恰好清除了那些标志位,造成了上面描述的现象。
&&& 有几个简单的办法解决这个问题:
& 1. 关闭寄存器或存储器显示窗口。
& 2. 在寄存器或存储器显示窗口中不显示这些敏感的寄存器。
& 3. 不要把断点放在对这些敏感的寄存器位操作的前面,以保证这些寄存器位不被调试软件意外地操作。
& 4. 看官自己添加~~~~~
五、在使用STM32的外设时,由于IO口被用作复用功能,但是外设的初始化正确,GPIO口初始化正确,外设的时钟也已开启,但是外设无法正常运行
&&& 其中最关键的一项,大多数使用者多没有设置,就是某个IO口被用作外设的接口时,需要开启IO口的复用功能的时钟,即进行外设、IO的时钟使能时,需要如下代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx | RCC_APB2Periph_AFIO, ENABLE);&&& /* GPIOx and AFIO clock enable */
x --- 为对应的GPIO口,如:A、B、C、D、E。
&&& 在使用时,一定要注意该要点!
发表评论:
TA的最新馆藏使用Keil MDK运行第一个STM32程序
由于Cnblogs使用livewriter发表文章排版比较方便,因此最近的博客首先通过cnblogs发表。
原创文章,转载请注明出处。
欢迎大家针对本博客文章提出宝贵意见。
关于使用固件库建立工程请参考
关于固件库的学习请参考
1.1.1 使用Keil MDK运行第一个STM32F10X程序
在上一小节中已经详细介绍了使用Keil MDK和标准外设库创建一个工程的过程,下面将介绍基于这个工程来编写一个小程序,通过这个程序我们可以初步了解:
l STM32标准外设库的简单使用过程
l STM32外设的使用方法和大致流程
l 程序的编译、链接、下载步骤
l 利用Keil MDK的在线仿真功能进行软件仿真的简要步骤
1. 程序的编写
(1)程序实现的功能
为了方便各位读者的入门和理解,这个小程序的功能非常简单,作为本书功能实践的第一个程序,其功能当然也是最为经典的“Hello World!”了,只不过不是简单的屏幕输出,而是利用硬件的串口进行输出,同时作为单片机类的第一个程序自然少不了LED闪烁的功能,这就是这个小程序的两个主要的功能:
l 利用串口1输出“Hello World!”字符。
l 控制两个LED闪烁。
(2)程序的实现
在基于标准外设库进行程序开发时一定要充分利用标准外设库下面的帮助文件stm32f10x_stdperiph_lib_um.chm以及库中自带的工程实例,同时结合STM32F10x系列的芯片手册来完成程序的开发,stm32f10x_stdperiph_lib_um.chm帮助文件如图 5-25所示。此文件中已经包含了标准外设库的全部内容,并根据根据内容结构进行了重新的编排和整理,更加方便程序的阅读和理解。
STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples文件夹下包括了众多外设的使用例程,这里的例程多是针对官方的开发板而编写,然而这些例程却非常全面的展示了相关外设的各种使用方法,对我们做基于标准外设库的开发有着非常重要的意义。
5-25 标准外设库的帮助文档
我们的第一个程序就通过这些参考来完成,首先来看串口部分。串口部分的详细信息可以参考本书第8章,这儿只进行简单的说明和介绍简便的开发方法,首先先找一个与我们使用的功能最近的一个例程,STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART文件夹下给出了多达12中的USART例程,这里选择较为相近的Interrupt文件夹下得例程,打开文件夹下面的mian.c文件,通过简单的浏览可以找到如下一段程序:
/* USARTy and USARTz configuration ------------------------------------------------------*/
/* USARTy and USARTz configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_N
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USARTy */
USART_Init(USARTy, &USART_InitStructure);
/* Configure USARTz */
USART_Init(USARTz, &USART_InitStructure);
/* Enable USARTy Receive and Transmit interrupts */
USART_ITConfig(USARTy, USART_IT_RXNE, ENABLE);
USART_ITConfig(USARTy, USART_IT_TXE, ENABLE);
/* Enable USARTz Receive and Transmit interrupts */
USART_ITConfig(USARTz, USART_IT_RXNE, ENABLE);
USART_ITConfig(USARTz, USART_IT_TXE, ENABLE);
/* Enable the USARTy */
USART_Cmd(USARTy, ENABLE);
/* Enable the USARTz */
USART_Cmd(USARTz, ENABLE);
上面的这段的程序配合注释可以很容易理解,USART_InitTypeDef定义了一个包括USART主要参数的结构体,因此首先对USART的相关参数进行配置,使用标准外设库进行配置的优势就体现出来了,通过程序可以很容易读出这个串口的配置:
l 波特率9600Kbps
l 数据长度8
l 奇偶校验:无
l 硬件流控制:无
l 工作模式:收、发
然后利用USART_Init函数进行初始化,这段程序中设置了两个串口,使用同样的配置,然后配置相应的中断。最后通过USART_Cmd函数使能相应的串口,前面有过介绍,这些例程里的程序是针对官方的开发套件的,因此程序中并没有指名具体的端口,而是使用了宏定义USARTy、USARTz。通过这段程序就可以很方便的更改相关的参数得到我们需要的配置程序。
这儿只是完成了USART的配置,下面来看一下对应的I/O设置,仍然在这个文件中可以找到GPIO_Configuration(void)这个函数,程序如下:
void GPIO_Configuration(void)
GPIO_InitTypeDef GPIO_InitS
#ifdef USE_STM3210C_EVAL
/* Enable the USART3 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);
/* Enable the USART2 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
#elif defined USE_STM3210B_EVAL || defined USE_STM32100B_EVAL
/* Enable the USART2 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
/* Configure USARTy Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTy_RxP
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTz_RxP
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
/* Configure USARTy Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTy_TxP
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTz_TxP
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
这段函数完成了相关的I/O配置,首先通过宏定义判断是官方的哪一款开发套件,然后进行相应的端口映射(端口映射请参加官方的数据手册),然后进行相应的端口配置,同串口配置一样,这段程序中德端口用的也是宏定义USARTy_RxPin替代的,改为我们使用的实际I/O,端口时钟设置为50MHz,串口所使用到的端口设置为复用(GPIO_Mode_AF_PP)完成端口初始化。
另外仍然通过观察这个例程可以很容易发现,在使用一个外设时还需要首先打开对应的外设时钟,这部分程序如下:
void RCC_Configuration(void)
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(USARTy_GPIO_CLK | USARTz_GPIO_CLK | RCC_APB2Periph_AFIO, ENABLE);
#ifndef USE_STM3210C_EVAL
/* Enable USARTy Clock */
RCC_APB2PeriphClockCmd(USARTy_CLK, ENABLE);
/* Enable USARTy Clock */
RCC_APB1PeriphClockCmd(USARTy_CLK, ENABLE);
/* Enable USARTz Clock */
RCC_APB1PeriphClockCmd(USARTz_CLK, ENABLE);
这段程序中需要注意两点,首先,GPIO、USART等都是连在APB1、APB2两条总线上的,各外设具体的总线连接情况参见图 5-2,因此首先应该确定外设对应的总线,例如USART1是APB2总线,而USART2是APB1总线。其次使能相应的时钟时不光要使能对应的I/O端口,还要使能总线的复用端口,这点也容易忽略。
最后根据库中的例程,借鉴库中例程的编写风格,就可以得出我们需要的程序,程序在工程的mian.c中编写,函数如下:
#include &stm32f10x.h&
void USART_Configuration(void);
void GPIO_Configuration(void);
void Delay(__IO uint32_t nCount);
void USART1_Puts(char * str);
int main(void)
USART_Configuration();
GPIO_Configuration();
USART1_Puts(&Hello Wrold!\n&);
GPIOF-&BSRR = 0x;
Delay(0xAFFFF);
GPIOF-&BRR = 0x;
Delay(0xAFFFF);
USART1_Puts(&Hello Wrold!\n&);
void USART_Configuration(void)
GPIO_InitTypeDef GPIO_InitS
USART_InitTypeDef USART_InitS
//使能串口、串口所用的I/O口以及端口复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
/* A9 USART1_Tx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽输出-TX
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* A10 USART1_Rx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入-RX
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_N
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable the USARTx */
USART_Cmd(USART1, ENABLE);
void GPIO_Configuration(void)
GPIO_InitTypeDef GPIO_InitS
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
/* 设置LED对应的引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
void Delay(__IO uint32_t nCount)
for(; nCount != 0; nCount--);
void USART1_Puts(char * str)
while(*str)
USART_SendData(USART1, *str++);
/* Loop until the end of transmission */
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
至此完成了主函数的编写。接下来还需要到stm32f10x_conf.h文件中选择相应的头文件,这儿去掉需要使用的头文件之前的注释,去掉注释的头文件如下:
l #include &stm32f10x_gpio.h&
l #include &stm32f10x_gpio.h&
l #include &stm32f10x_usart.h&
这样,我们的程序编写就完成了,下面可以进行我们的编译与调试了。
2. 程序的编译
在Keil MDK的工具栏上自左至右依次有三个图标,分别为编译指定文件、生成工程、重新生成所有工程。首先在mian.c下点击编译图标,如果编译没有错误,则输出窗口如图
5-26所示。如果编译存在错误,点击错误的地方可以直接定位到错误行,根据错误提示进行修改。
5-26 编译输出窗口
编译不存在错误后点击图标生成工程,生成没有错误则输入如图
5-27所示。如果存在错哦无则可以直接点击对应的错误跳转到对应的文件对应的行,根据错误提示进行再次修改。
5-27 生成工程输出窗口
第一次生成工程成功以后,如果修改了相应的文件再次点击生成工程时只会对有改动的文件进行重新编译、生成,而重新生成全部工程会重新生成工程中的所有文件。
3. 程序的调试
在实际开发时,可以首先利用软件进行仿真,利用软件进行仿真查看具体的寄存器配置,外设的工作情况等,同时通过断点的配合可以查找出程序中绝大多数的错误。软件仿真完成后可以下载到硬件平台去运行,如果程序仍然没有达到需要的效果,在硬件检查完成后可以使用在线仿真的方式,在硬件平台上直接运行程序,进一步查找问题。
首先,先看一下这个小程序的软件仿真,首先在工程设置Debug选项中选择使用软件模拟,如图 5-28所示。
5-28 Debug设置选项窗口
点击图标,或者从菜单栏-Debug启动调试。启动后界面如图
5-29所示。调试启动后自动运行到主程序。
5-29 调试主界面
在工具栏可以找到与调试相关的快捷图标,同样在菜单栏Debug菜单下可以找到对应的选项,自左向右分别为复位,运行,点击后直接运行程序;停止,运行时点击停止当前运行;单步,单步执行当前程序;跳过,可以直接跳过子函数的运行;跳出,在某个子函数中运行时可以直接跳出当前函数;运行到光标位置处。
在程序行号的左侧直接双击可以非常方便的添加或者取消断点,如图 5-30所示。
5-30 断点的添加与取消
在菜单Peripherals中可以打开需要查看的外设,这里点击Peripherals-USART1弹出USART1的相关观察窗口,点击运行后,窗口如图 5-31所示,就会显示运行的程序对USART1所完成的相关配置。从中可以清晰的看出,程序对USART1的相关寄存器配置,从地面Settings一栏中则可以直观的看出USART1当前的工作状态,从这儿就可以检查我们的相关参数设置正确与否,对应时钟有无开启等。需要说明的是这里寄存器在进行仿真的时候是可以进行改写的,也就是说允许我们在程序运行的时候直接进行相关的操作来观察运行结果。但是此时程序本身也是可以对寄存器进行操作的,因此直接更改相应的寄存器观察时还要注意你的操作是否和程序冲突。例如在本程序的运行过程中如果将寄存器中发送使能TE的勾给去掉,则串口就不会在输出数据。
5-31 USART1寄存器观察窗口
接下来继续查看I/O的配置,本程序中使用的是F端口,则点击菜单Peripherals-General-Purpose I/O,选择GPIOF,出现GPIOF的配置窗口,程序运行后可以通过窗口查看GPIOF的相关配置,如图 5-32所示。在这个窗口中可以查看引脚的配置情况,当程序运行时就能看到对应引脚的勾在闪烁。
5-32 GPIOF寄存器观察窗口
通过这些操作,可以非常方便、直观的查看程序运行后对应的配置是否与我们预想的一致,那么在本程序中通过串口输出了“Hello World!”又该怎样查看运行结果呢?在工具栏中可以非常方便的打开相应的查看窗口,查看相关寄存器和外设等。由于我们需要使用串口1输出字符,同时LED闪烁显示,因此首先打开串口1的输出,点击图标,弹出如所示下拉菜单,选择UART#1如图
5-33所示,或者点击View菜单点击Serial Windows选择UART#1即可打开UART的输出窗口,运行程序后会在此窗口中输出串口信息,如图 5-34所示。
5-33 打开UART1输出窗口
5-34 UART1输出窗口
至此,已经利用Keil MDK的软件仿真功能完成了程序的调试,看到了设计的效果。通过这个例子也能够让我们深刻的体会到Keil MDK在线调试功能的强大之处。接下来我们将编写好的程序下载到开发板中运行,看看运行效果。
4. 程序的下载
编写好的程序工程生成完成后下载到硬件平台的方法有很多种,一是直接利用Flash烧写工具烧写相应的hex文件,在工程配置时需要设置才能生成hex文件,具体设置参加图 5-16,生成hex文件之后就可以利用烧写工具烧写到硬件平台,常用的仿真器都带有相应的软件工具,如笔者使用的JLink-V8,可以使用自带的J-Flash工具,使用J-Flash工具如图 5-35所示,在工程管理中选择所使用的硬件平台,点击Target-Connect后就可以连接上硬件平台,在图中左侧显示了仿真器以及对应的硬件平台信息。然后选择所生产的hex文件,就可以讲编译的程序下载到硬件平台中。
5-35 J-Flash主界面
这种下载方式非常适合将程序源码不公开的情况下将程序固件提供给第三方使用,然而在使用Keil MDK时还有更为方便的方式,在工程设置中设置好了下载工具后可以点击图标,直接将程序下载到硬件平台中运行,非常方便。在使用仿真器进行在线仿真时也可以通过设置在在线仿真时将程序下载到flash中。
程序下载到硬件平台后通过实际的串口来查看程序运行情况了,将硬件平台的串口1连接至PC,笔记本等电脑上没有物理串口的可以使用USB转串口设备,注意在设备管理器中查看实际所分配的串口号,本机分配的串口号为COM4,如图 5-36所示,串口号也可以在高级选项中进行更改。
5-36 设备管理器中查看串口号
这里使用本章所介绍的TKStudio中的串口调试助手工具来进行查看,设置串口的相应参数与程序一致,串口设置于运行结果如图 5-37所示,从图中可以看出串口接数据正确,开发板上的两个LED也在不停闪烁。
5-37 串口调试助手运行界面
至此,我们完成了一个简单的程序开发过程。本小节通过实现串口输出与LED闪烁的功能系统的介绍了怎样基于标准外设库进行程序的开发与调试,这儿只是列举了一个非常简单的小例子,更多的联系读者可以参考标准外设库中的例程自行完成。
看过本文的人也看了:
我要留言技术领域:
收藏提示你已经自动关注本知识库了哦!
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?

我要回帖

更多关于 keil软件仿真教程 的文章

 

随机推荐