34回答

8收藏

STM32F4从入门到深入,很好的教程系列转过来一起学习!

 

#竞赛 #竞赛 54560 人阅读 | 34 人回复 | 2013-07-26

本帖最后由 奋斗哥 于 2013-7-26 14:16 编辑

STM32F4从入门到深入,很好的教程系列转过来一起学习!
作者博客地址:http://blog.csdn.net/w471176877
文档下载请见13#

一、认识硬件

STM32F4-Discovery


STM32F4-Discovery基于STM32F407VGT6,板上包括了一个ST-LINK/V2 调试工具和2 个ST MEMS、USB OTG接口等。以及大量的软件例程。

官方F4网站:http://www.st.com/stm32f4-discovery


1.     STM32F407VGT6微控制器:32位 ARMCortex-M4F 内核, 1 MB Flash,192KB RAM ,LQFP100封装。

2.     板上ST-LINK/V2调试器/编程器:板上的ST-LINK/V2可以单独使用,调试/编程用户自己的目标板,其接口是SWD接口。

3.     USB供电或者是外部5V供电

?外部电源:3.0V ~ 5.0V

4.  LIS302DL:ST MEMS 三轴加速度传感器

5.     CS43L22:集成了D类扬声器驱动器的音频DAC

6.  8个LED

  LD1 (red/green) :USB通信指示灯

  LD2 (red) :3.3 V 电源指示灯

   4个供用户使用的LEDs, LD3 (orange), LD4 (green), LD5 (red) and LD6 (blue)

   2 USB OTG LEDs LD7 (green) VBus and LD8 (red) over-current


STM32F407VGT6微控制器的时钟

复位后默认选择16 MHz的内部RC振荡器作为时钟。这个RC振荡器有1%的精度,用户也可以选择外部的RC振荡器或4-26 MHz的时钟源,如果检测到这个时钟出现故障,系统将会自动切换回内部RC振荡器并产生一个软件中断(如果启用)。

该时钟源输入到一个PLL从而允许陪频到168 MHz。

通过多个预分频器可以分别配置的两条AHB总线,高速总线APB(APB2)和低速总线APB(APB1)。AHB最高频率为168M,高速APB的最高频率为84M,低速APB最高频率为42M。
        芯片内嵌入了另一个PLL(PLLI2S)从而允许I2S主时钟产生所有从8 kHz至192 kHz的频率。

系统时钟(SYSCLK)可以从以下三个不同的时钟源中选择:

HSI指振荡器时钟

HSE振荡器时钟

锁相环(PLL)时钟

两个可选低速时钟源(用于实时时钟和独立看门狗):

         内置32K时钟

         外接32.768K时钟


分享到:
回复

使用道具 举报

回答|共 34 个

倒序浏览

沙发

奋斗哥

发表于 2013-7-26 10:39:01 | 只看该作者

本帖最后由 xinxincaijq 于 2013-7-26 10:40 编辑

二、创建工程

         首先介绍一下IAR。

         IAR EmbeddedWorkbench for ARM是一套支持ARM所有处理器的集成开发环境,包含项目管理器、编辑器、C/C++编译器、汇编器、连接器和调试器。在IAR Embedded Workbench for ARM环境下可以使用C/C++和汇编语言方便地开发ARM嵌入式应用程序,比较其他的ARM开发环境,IAREmbedded Workbench for ARM具有入门容易、使用方便、代码紧凑等特点。通过其内置的针对不同芯片的代码优化器,IAR Embedded Workbench for ARM可以为ARM芯片生成非常高效和可靠的FLASH/PROMable代码。不仅有这些可靠的技术,IAR Systems还为您提供专业的全球技术支持。(此段内容来自IAR网站)

然后我们一步步创建一个工程。

1.     新建一个文件夹,命名为工程的名字,在这个文件夹里创建我们的工程。为了更好管理工程,我们先在此文件夹下新建三个文件夹,分别命名为user,inc,和startup,如下图所示:

2.     到st网站上下载这块板子的库函数例程:http://www.stmcu.org/download/index.php?act=down&id=723

3.     解压后把STM32F4-Discovery_FW_V1.1.0\Project\Demonstration\EWARM目录下的stm32f40x_flash.icf复制到新建的文件夹目录下

把STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\Include目录下的core_cm4.h

core_cm4_simd.h  core_cmFunc.h  core_cmInstr.h复制到inc目录下,

然后在把STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\ST\STM32F4xx\Include目录下stm32f4xx.h和system_stm32f4xx.h文件复制到inc目录下。inc目录下的文件如下图所示:


4.     把STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\ST\STM32F4xx\Source\Templates\iar目录下的startup_stm32f4xx.s复制到startup目录下。

5.     把STM32F4-Discovery_FW_V1.1.0\Libraries\CMSIS\ST\STM32F4xx\Source\Templates目录下的system_stm32f4xx.c文件复制到user目录下

6.     打开IAR,点击Project->Creat New Project,出现如下对话框:

点击OK,出现如下对话框:

找到刚才建立的文件夹,打开,然后在文件名中写入工程的名字,点击保存。

7.     然后右击Files下的工程,点击Add,按下图所示点击Add Group

出现如下对话框:

输入user,点OK。

重复步骤6,在建立两个Group,分别命名为startup和inc

8.     右击inc->Add->Files,如下图所示:

然后在对话框中打开刚才创建的inc文件夹,把其中的文件全部选中,如下图所示:

点击打开。

重复步骤7,在Group startup中添加文件夹startup中的startup_stm32f4xx.s文件,在Groupuser中添加文件夹user中的system_stm32f4xx.c文件,完成后如下图所示:

9.     点击File下的图标新建一个文件,我们的主程序就写在这个文件中,点击保存,命名为mian.c吧,保存在user目录下。

10.  然后加到Group user下


11.  右击工程Options弹出如下对话框

Target页按上图所示设置

点击C/C++ Compler,点击Prerocessor页,按下图设置

        

点击Linker勾选Overridedefault选项,然后点击右面浏览按钮

弹出如下对话框,点击但工程目录下,打开stm32f40x_flash.icf文件,如下图所示


点击Debugger,Driver选ST-LINK

点击Download页,勾选Use flashloader选项,如下图所示


点击ST-LINK,Interface选择SWD

点击OK,工程设置完毕

因为discovery的晶振为8M,所以请在 stm32f4xx.h中改
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */

在system_stm32f4xx.c中改
#define PLL_M 8


在main.c中写入如下代码:

//file:main.c

  1. #include<stm32f4xx.h>



  2. void main ()

  3. {

  4.   while(1);

  5. }
复制代码

按键盘F7键,编译连接没出错的话,就是工程建立成功了,以后就可以直接复制这个工程文件夹,然后再添加一些代码或者文件来创建新的工程了。




板凳

奋斗哥

发表于 2013-7-26 10:40:54 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 11:19 编辑

三、了解编程环境

        前文再续,书接上一回,前面我们建立了一个文件夹,用来存放我们的工程,有建了三个文件夹,现在,分别解析一下这三个文件夹里放的是什么东西。

inc文件夹里放的是头文件,一共六个,以后我们自己的头文件也可以放到这里来。

stm32f4xx.h                     包含了stm32f4的寄存器结构体的定义(类似于C51的reg52.h)

core_cm4.h                      内核功能的定义,比如NVIC相关寄存器的结构体

core_cmInstr.h                 包含一些内核核心专用指令

core_cmFunc.h                内核核心功能接口头文件

core_cm4_simd.h           包含与编译器相关的处理

system_stm32f4xx.h      system_stm32f4xx.c的头文件

         user里面放的是system_stm32f4xx.c文件和用户自己定义的文件(比如main.c)。system_stm32f4xx.c里面是系统初始化的设置,包括时钟的配置等

         startup里放的startup_stm32f4xx.s是启动文件,是一个汇编文件。不同的编译器应该用不同的启动文件。它的作用包括:

1.     设定SP的初值

2.     设置PC的初值

3.     设置中断向量表的地址

4.     配置时钟

5.     设置堆栈

6.     调用mian等

这个启动文件是先调用system_stm32f4xx.c里面的SystemInit()在调用main的。

然后就是IAR的操作。

以下这几个按钮时最常用的

第一个是编译

第二个是生成可执行文件

第三个是设置断点

第四个是下载调试

第五个是在线调试

我们写好程序,然后按编译,在按第二个,如果编译没错,连接提示错误的话,原因很可能是之前Linker configuration file的文件没选对,或者一开始的芯片类型没选对等

OK,这就是我们的编程环境。

地板

奋斗哥

发表于 2013-7-26 10:41:35 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 11:22 编辑

四、GPIO


       stm32f407VG这款单片机上共有PA-PE共5×16共80个复用IO口,每个通用I / O端口有4个32位配置寄存器(GPIOx_MODERGPIOx_OTYPER GPIOx_OSPEEDR GPIOx_PUPDR),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位的置位/复位寄存器(GPIOx_BSRR),32位锁定寄存器(GPIOx_LCKR)和两个32位的备用功能选择寄存器(GPIOx_AFRHGPIOx_AFRL)。

设置步骤:

1.     使能相关时钟

2.     设置相应的IO口为输入或输出

3.     设置输入\输出的类型

4.     设置输出的速度(如果设置为输入,此步跳过)

5.     如果输出速度>=50M,开启补偿单元

6.     设置上拉下拉寄存器

7.     通过复位置位寄存器和输入输出数据寄存器进行操作。



程序:

  1. /************************************
  2.     标题:操作GPIO的练习
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     author:小船
  8.     data:2012-01-16
  9. *************************************/

  10. #include <stm32f4xx.h>

  11. uint32_t Gb_TimingDelay;

  12. void Delay(uint32_t nTime);

  13. void main ()
  14. {   
  15.   SysTick_Config(SystemCoreClock / 1000);         //设置systemtick一毫秒中断
  16.   
  17.   RCC->AHB1ENR |= 0x00000008;         //使能GPIOD时钟
  18.   RCC->APB2ENR |= (1<<14);          //使能syscfg时钟
  19.   
  20.   GPIOD->MODER &= 0x00FFFFFF;         //设置PD12,13,14,15输出
  21.   GPIOD->MODER |= 0x55000000;
  22.   
  23.   GPIOD->OTYPER &= 0xFFFF0FFF;         //设置PD12,13,14,15推挽输出
  24.   
  25.   GPIOD->OSPEEDR &= 0x00FFFFFF;         //PD12,13,14,15 速度100m
  26.   GPIOD->OSPEEDR |= 0xff000000;
  27.   
  28.   SYSCFG->CMPCR = 0x00000001;         //使用IO补偿单元,
  29. //当GPIO速度超过50M的时候要考虑使用此设置
  30.   
  31.   GPIOD->PUPDR &= 0x00FFFFFF;          //PD12,13,14,15 无上拉无下拉
  32.   
  33.   GPIOD->BSRRH = 0xf000;          //reset register GPIOx_BSRRH, write only
  34.                                   //set register GPIOx_BSRRL, write only
  35.   
  36.   while(1)
  37.   {
  38.       GPIOD->BSRRH = 0xf000;
  39.       GPIOD->BSRRL = 0x1000;
  40.       Delay(500);
  41.       
  42.       GPIOD->BSRRH = 0xf000;
  43.       GPIOD->BSRRL = 0x1000<<1;
  44.       Delay(500);
  45.       
  46.       GPIOD->BSRRH = 0xf000;
  47.       GPIOD->BSRRL = 0x1000<<2;
  48.       Delay(500);
  49.       
  50.       GPIOD->BSRRH = 0xf000;
  51.       GPIOD->BSRRL = 0x1000<<3;
  52.       Delay(500);
  53.   }
  54. }

  55. void Delay(uint32_t nTime)
  56. {
  57.   Gb_TimingDelay = nTime;

  58.   while(Gb_TimingDelay != 0);
  59. }


  60. void SysTick_Handler(void)
  61. {
  62.   if (Gb_TimingDelay != 0x00)
  63.   {
  64.     Gb_TimingDelay--;
  65.   }
  66. }
复制代码
5#

奋斗哥

发表于 2013-7-26 10:42:02 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:34 编辑

五、NVIC


       中断向量嵌套控制器是用来管理所有中断和事件的,包括中断的使能和除能,中断的优先级。这个是属于内核的东西,所以ST的参考手册上对它的描述较少,但他又是十分重要的东西,要了解它就要看ARM的《Cortex?-M4 Devices Generic User Guide》。

相关寄存器

译自《Cortex?-M4 Devices Generic UserGuide》,若有错误,请以原文为准。

中断使能寄存器NVIC_ISER[8]

中断使能寄存器共有8个,ISER[0]设置0~31号中断的使能,ISER[1]设置32~63号中断的使能,如此类推。以下以ISER[0]为例:

[31:0] SETENA中断设置使能位。
写:
0 =无影响
1 =使能中断。
读:
0 =中断是禁止的
1=中断已经被使能

如果要使能0号中断,就向该寄存器的0位写1,如果要使能38号中断,就向NVIC_ISER[1]的6位写1,如此类推,至于哪个中断对应哪个中断号,请参见参考手册《RM0090 Reference manual》中的第9章Table 30. Vector table的Position一列。

中断除能寄存器NVIC_ICER[8]

中断除能寄存器共有8个,ICER[0]设置0~31号中断除能,ICER[1]设置32~63号中断的使能,如此类推。以下以ICER[0]为例:


[31:0] SETENA中断设置使能位。
写:
0 =无影响
1 =除能中断。
读:
0 =中断是禁止的
1=中断已经被使能

以下寄存器均为8个,仅以***R[0]为例

中断挂起设置寄存器NVIC_ISPR[8]

[31:0]SETPEND中断挂起设置位。
写:
0 =无影响
1 =改变中断状态为挂起。
读:
0 =中断没有挂起
1 =中断正在等待处理。

解除中断挂起寄存器NVIC_ICPR[8]

[31:0] CLRPEND中断清除挂起位。
写:
0 =无影响
1 =删除中断的挂起状态。
读:
0 =没有挂起的中断
1 =中断正在等待处理。

中断激活位寄存器NVIC_IABR[8]

[31:0]中断活跃的标志:
0 =中断不活跃
1 =中断活跃。

如果相应的中断的状态是作为一个活跃的或活跃和正被挂起的,读该位将会读出1。

中断优先级寄存器NVIC_IPR[60]

       中断优先级寄存器为60个32位寄存器,st的结构体中用了240个8位的字节数组NVIC->IP[240]来映射,每一个对应一个中断的优先级。

ARM的中断优先级分两种,抢占优先级和响应优先级。

        具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

         当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

        中断优先级分组就是把优先级寄存器分割,分开哪几位是响应优先级,哪几位是抢占优先级。至于怎样设置分组,就要看一个不属于NVIC的寄存器了。

应用中断和复位控制寄存器SCB_AIRCR

在这里我们需要看的是[31:16]位和[10:8]位,[31:16]位是识别码,用以保护此寄存器不会被意外修改,[10:8]位就是中断优先级分组的设置位。

[31:16]

写:VECTKEYSTAT
读:VECTKEY
RW注册键:
读为0xFA05
写入时,要写0x05FA到 VECTKEY,否则写入将被忽略。

[10:8]中断优先级


每一个ARM的M4中断优先级设计为可编程的8位,具体到stm32f4就只留给用户4位共16级的可编程优先级,其中低4位已被占用。也就是说stm32f4的优先级分组情况如下表所示:

[10:8]
分割点
抢占优先级位
响应优先级位
抢占优先级数
响应优先级数
0b011
xxxx
[7:4]
none
16
1
0b100
xxx.y
[7:5]
[4]
8
2
0b101
xx.yy
[7:6]
[5:4]
4
4
0b110
x.yyy
[7]
[6:4]
2
8
0b111
yyyy
none
[7:4]
1
16

最后一个NVIC的寄存器

软件触发中断寄存器NVIC_STIR

当SCB_CCR的USERSETMPEND位为1时,无特权的用户程序才能写此寄存器。

[31:9]保留。
[8:0] INTID  ID号中断触发,
范围0-239。例如,0x03的指定中断IRQ3触发。

6#

奋斗哥

发表于 2013-7-26 10:42:53 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:36 编辑

六、EXTI



       外部中断/事件控制器包括用于生成高达23个事件/中断的请求。每条中断线可独立配置选择类型(响应或挂起)和相应的触发事件(上升或下降或两者)。每一中断线可以独立屏蔽。有一个挂起寄存器维持中断线的状态请求。

EXTI线0~15连接到PX0~15(可编程配置)

EXTI线16连接到PVD的输出事件
EXTI线17连接到RTC报警事件
EXTI线18连接的USB OTG FS唤醒事件
EXTI线19连接到以太网唤醒事件
EXTI线20连接的USB OTG HS(在FS配置)唤醒事件
EXTI线21连接到RTC的篡改和TimeStamp事件
EXTI线22连接到RTC唤醒事件


设置步骤:

1.     使能相关时钟

2.     设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

3.     设置中断线所要连接到的IO的输入模式

4.     设置屏蔽寄存器,不能把中断屏蔽掉

5.     设置中断方式

6.     把中断线连接到IO口

7.     设置中断优先级

8.     使能中断线

9.     编写中断服务函数(函数名是固定的)

10.  中断服务函数里检查是哪个中断挂起

11.  编写相应的程序

12.  清除中断挂起




程序:

  1. /************************************
  2.     标题:操作EXTI的练习
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     author:小船
  8.     data:2012-01-17
  9. *************************************/

  10. #include <stm32f4xx.h>

  11. void Led_Init (void);

  12. void main ()
  13. {   
  14.   
  15.   RCC->AHB1ENR |= 0x00000009; //使能GPIOD和GPIOA时钟
  16.   RCC->APB2ENR |= (1<<14);  //使能syscfg时钟

  17.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  18.   
  19.   Led_Init();
  20.   
  21.   GPIOA->MODER &= 0xFFFFFFFC; //浮空输入模式
  22.   GPIOA->PUPDR &= 0xFFFFFFFC;
  23.   
  24.   EXTI->IMR |= ( 1 << 0 );  //不屏蔽外中断线0
  25.   
  26.   EXTI->FTSR |= ( 1 << 0 ); //下降沿触发
  27.   
  28.   SYSCFG->EXTICR[1] &= 0xFFFFFF00;  //配置外中断线0到PA口
  29.   
  30.   NVIC->IP[6] = 0xe0;   //最低抢占优先级,最高响应优先级1110
  31.   
  32.   NVIC->ISER[0] |= (1<<6);  //使能中断线6,也就是外中断0
  33.   
  34.   
  35.   while(1)
  36.   {
  37.   };
  38. }


  39. void Led_Init()
  40. {
  41.   GPIOD->MODER &= 0x00FFFFFF; //设置PD12,13,14,15输出
  42.   GPIOD->MODER |= 0x55000000;
  43.   
  44.   GPIOD->OTYPER &= 0xFFFF0FFF; //设置PD12,13,14,15推挽输出
  45.   
  46.   GPIOD->OSPEEDR &= 0x00FFFFFF; //PD12,13,14,15 速度100m
  47.   GPIOD->OSPEEDR |= 0xff000000;
  48.   
  49.   GPIOD->PUPDR &= 0x00FFFFFF;  //PD12,13,14,15 无上拉无下拉
  50.   
  51.   GPIOD->BSRRH = 0xf000;  //reset register GPIOx_BSRRH, write only
  52.                           //set register GPIOx_BSRRL, write only
  53.   SYSCFG->CMPCR = 0x00000001; //使用IO补偿单元
  54. }

  55. void EXTI0_IRQHandler(void)
  56. {
  57.   uint32_t tmp;
  58.   if(EXTI->PR & 0x00000001 != 0)  //检查是不是外中断0挂起
  59.   {
  60.     tmp = (~GPIOD->ODR) & 0x0000f000;   //PD15 14 13 12翻转,板子上四个LED的亮灭
  61.     GPIOD->ODR &= 0xffff0fff;
  62.     GPIOD->ODR |= tmp;
  63.    
  64.     EXTI->PR = (1<<0);  //外中断0解除挂起
  65.   }
  66. }
复制代码
7#

奋斗哥

发表于 2013-7-26 10:43:13 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:40 编辑

七、USART

STM32F407xx内嵌四个通用同步/异步接收器??(USART1,USART2,USART3和USART6)和两个通用异步收发器(UART4和UART5)。这6个接口提供异步通信的IrDASIR ENDEC支持,多机通信模式,单线半双工通信模式LIN主/从功能。 USART1和USART6接口能够速度高达10.5 Mbit / s的通信其他可用的接口通信高达5.25bit/s。USART1,USART2,USART3和USART6还提供硬件管理的CTS,RTS信号,智能卡的模式(ISO7816兼容)和类似的SPI通信能力。所有接口都可以通过DMA控制器。

?USART name
Standard features
Modem (RTS/CTS)
LIN
SPI master
irDA
Smartcard (ISO 7816)
Max. baud rate in Mbit/s (oversampling by 16)
Max. baud rate in Mbit/s (oversampling by 8)
APB mapping
USART1
X
X
X
X
X
X
5.25
10.5
APB2 (max. 84 MHz)
USART2
X
X
X
X
X
X
2.62
5.25
APB1 (max. 42 MHz)
USART3
X
X
X
X
X
X
2.62
5.25
APB1 (max. 42 MHz)
UART4
X
-
X
-
X
-
2.62
5.25
APB1 (max. 42 MHz)
UART5
X
-
X
-
X
-
2.62
5.25
APB1 (max. 42 MHz)
USART6
X
X
X
X
X
X
5.25
10.5
APB2 (max. 84 MHz)

这里只介绍两根线的最简单串口设置。

波特率的计算:

         USART用的波特率是由APB时钟线和波特率寄存器USART_BRR确定的,USART_BRR为32位寄存器,其中高16位保留,低十六位确定波特率,低16位又分为[15:4]和[3:0]或[15:4]和[2:0],具体分法由OVER8决定,[15:4]存放USARTDIV的整数部分,小数部分由[3:0]或[2:0]存放。



小数部分USARTDIV的小数部分乘以16或8,结果保留整数存于USART_BRR[3:0]或[2:0]中

比如:要求波特率为115200


设置OVER8=0

解得:USARTDIV=22.768

USART_BRR[15:4]=22=0x16

USART_BRR[3:0]=0.768*16=13=0xC

USART_BRR=0x0000016C

设置步骤:

1.     设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

2.     使能相关时钟。

3.     设置波特率。

4.     设置控制寄存器CR。

5.     选择相关GPIO引脚的复用功能。

6.     设置相关GPIO引脚为复用模式。

7.     设置相关GPIO引脚的速度,方式。

8.     如果要用到中断,设置USART中断优先级。

9.     如果要用到中断,使能USART中断。

10.  如果要用中断,编写中断服务函数(函数名是固定的)。

11.  中断服务函数里检查是哪个中断。

12.  编写相应服务程序。


电路参见:小工具之——max232电平转换

PCB尺寸:58mm*56mm






程序:

  1. /*********************************************
  2.     标题:操作USART的练习
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     描述:从其他设备接收数据,再把数据发送出去

  8.     author:小船
  9.     data:2012-02-01
  10. **********************************************/

  11. #include <stm32f4xx.h>

  12. u32 Gb_TimingDelay;

  13. u8 suffer[100];
  14. u8 ok_to_send;
  15. u8 Rx_data_counter;

  16. void Delay(uint32_t nTime);

  17. void main ()
  18. {
  19.   
  20.   char Tx_data_counter;
  21.   
  22.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  23.   SysTick_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断
  24.   
  25.   RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟
  26.   RCC->APB1ENR |= (1<<18);  //使能usart3时钟
  27.   
  28.   USART3->BRR = 0x0000016C;   //波特率115200

  29.   /*
  30.   使能usart3
  31.   usart3发送使能
  32.   usart3接收使能
  33.   接收缓冲区非空中断使能
  34.   8bit
  35.   一位停止位
  36.   无校验
  37.   */
  38.   USART3->CR1 |= (( 1<<13 ) | ( 1<<3 ) | ( 1<<2 ) | ( 1<<5 ));
  39.   
  40.   GPIOD->AFR[1] |= 0x00000077;//选择PD8,9复用功能
  41.   
  42.   GPIOD->MODER &= 0xFFF0FFFF; //设置PD8,9,复用模式
  43.   GPIOD->MODER |= 0x000A0000;
  44.   
  45. //  GPIOD->OTYPER &= 0xFFFFDFFF; //设置PD9推挽输出
  46.   
  47.   GPIOD->OSPEEDR &= 0xFFFCFFFF; //PD8速度50m
  48.   GPIOD->OSPEEDR |= 0x00020000;
  49.   
  50.   GPIOD->PUPDR &= 0xFFFCFFFF; //PD8
  51.   GPIOD->PUPDR |= 0x00010000;
  52.   
  53.   NVIC->IP[39] = 0xf0; //最低抢占优先级,最低响应优先级1111
  54.   NVIC->ISER[1] |= (1<<(39-32)); //使能中断线39,也就是usart3中断
  55.   
  56.   while(1)
  57.   {  
  58.     if(ok_to_send)  //接收到数据,可以将数据发送
  59.     {
  60.       if((USART3->SR & (1<<7))) //发送数据寄存器空
  61.       {
  62.         USART3->DR = suffer[Tx_data_counter];
  63.         Tx_data_counter++;
  64.         if( suffer[Tx_data_counter] == '\r' )
  65.         {
  66.           Tx_data_counter = 0;
  67.           USART3->CR1 |= 1<<5;  //使能接收中断
  68.           ok_to_send = 0;
  69.         }
  70.       }
  71.     }
  72.   }
  73. }


  74. void Delay(uint32_t nTime)
  75. {
  76.   Gb_TimingDelay = nTime;

  77.   while(Gb_TimingDelay != 0);
  78. }

  79. void SysTick_Handler(void)
  80. {
  81.   if (Gb_TimingDelay != 0x00)
  82.   {
  83.     Gb_TimingDelay--;
  84.   }
  85. }

  86. void USART3_IRQHandler(void)
  87. {
  88.   if(USART3->SR & (1<<5)) //接收数据寄存器非空
  89.   {
  90.     suffer[Rx_data_counter] = USART3->DR;
  91.     Rx_data_counter++;
  92.     if(suffer[Rx_data_counter - 1] == '\r')
  93.     {
  94.       Rx_data_counter = 0;
  95.       USART3->CR1 &= ~(1<<5); //除能接收中断
  96.       ok_to_send = 1;
  97.     }
  98.    }
  99. }
复制代码

运行结果:

1347247879_5323.png (20.81 KB, 下载次数: 131)

1347247879_5323.png
8#

奋斗哥

发表于 2013-7-26 10:43:32 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:42 编辑

八、DMA

       直接内存访问(DMA)是用来以提供外设和内存、内存和内存之间的高速数据传输的。数据可以在没有任何CPU干预下通过的DMA进行传输。这使得CPU资源更倾重与其他操作。

       DMA控制器基于一个复杂的总线矩阵架构,结合了功能强大的双AHB主总线架构与独立的FIFO,以优化系统带宽。

        两个DMA控制器共有16个数据流(stream),每个数据流可以编程与规定的通道中的一个搭配。


DMA的工作模式

1.  单次传输

2. 多次传输(burst):把数据分成多次传输

DMA的工作模式

1. 循环模式:循环模式是可用来处理循环缓冲区和连续的数据流(如ADC扫描模式)。启此功能可以设置DMA_SxCR寄存器的CIRC位启用。

在循环模式,在burst方式下,它必须遵循下面的规则

DMA_SxNDTR 等于 ((Mburst beat) × (Msize)/(Psize))的整数倍。

2.     双缓冲模式:双缓冲模式通过设置在DMA_SxCR寄存器的DBM位启用。

双缓冲模式与单缓冲模式的区别在于它有两个地址,当栓缓冲模式被使能,循环模式会被自动使能,每次传输完成,内存地址将会被交换。当一个内存区域被DMA控制器使用时,另一个可供程序使用。

如果需要改变内存地址,需要遵循以下规则:

DMA_SxCR 寄存器的CT 位为 0时,DMA_SxM1AR寄存器可以被改变

DMA_SxCR 寄存器的CT 位为 1时,DMA_SxM0AR寄存器可以被改变


    设置步骤:

1.     使能相关时钟。

2.     如果数据流已启用,通过重置在DMA_SxCR寄存器的EN位禁用它,然后读取该位,以确认有没有持续的数据流操作。向该位写0不会立即生效,因为它实际上是在全部转移完成时才被清除的。当EN位读为0,这意味着数据流是准备好,可以进行配置了。因此,任何数据流配置之前,有必要等待EN位被清除。在数据流重新启动前,前一次DMA传输中的状态寄存器(DMA_LISR and DMA_HISR)所有数据流专用的位应该被清除。

3.      在DMA_SxPAR寄存器中设置外设端口寄存器的地址。

4.     在DMA_SxMA0R寄存器中设置内存的地址(在双缓冲模式的情况下,还需在DMA_SxMA1R寄存器中设置内存的地址)。

5.     在DMA_SxNDTR寄存器中设置传输的数据总数,每个外设的事件或每次burst传输之后,这个值是将会递减。

6.      在DMA_SxCR寄存器使用CHSEL[2:0] 选择DMA通道。

7.      如果外设为流量控制器,它支持此功能,设置DMA_SxCR寄存器中PFCTRL的位。

8.      在DMA_SxCR寄存器的PL[1:0]位配置数据流优先级。

9.     配置FIFO(启用或禁用,发送和接收的阀值)。

10.  在DMA_SxCR寄存器中配置数据传输的方向,外设和内存递增/固定的模式,单个或burst传输,外设和存储器的数据宽度,循环模式,双缓冲模式和完成几分之几后中断。

11.   激活设置数据流在DMA_SxCR寄存器的EN位。

12.  使能相关外设寄存器的DMA模式位,启动传输。

程序:

  1. /*********************************************
  2.     标题:操作DMA的练习
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     描述:从其他设备接收数据,再把数据发送出去
  8.           USART3接收中断,发射用DMA
  9.     author:小船
  10.     data:2012-02-03
  11. **********************************************/

  12. #include <stm32f4xx.h>

  13. u8 USART_DMA_Completed;
  14. u8 Rx_Completed;
  15. u8 Rx_data_counter;
  16. u8 usart3_buffer[100];

  17. void USART3_DMA_config(void);
  18. void USART3_config(void);

  19. void main ()
  20. {
  21.   
  22.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  23.   
  24.   RCC->AHB1ENR |= ( (1<<3) | (1<<21) ); //使能GPIOD时钟,使能DMA1时钟
  25.   RCC->APB1ENR |= (1<<18);  //使能usart3时钟
  26.   
  27.   USART3_DMA_config();
  28.   USART3_config();
  29.   
  30.   USART_DMA_Completed = 1;
  31.   
  32.   while(1)
  33.   {  
  34.     if(USART_DMA_Completed & Rx_Completed)  //之前数据已经发送完成,接收到新的数据
  35.     {  
  36.       DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
  37.       while(DMA1_Stream3->CR & 0x00000001);//确保DMA可以被设置     
  38.       DMA1->LIFCR |= 0x0f800000;//传送前清空DMA1_Stream3所有中断标志      
  39.       DMA1_Stream3->NDTR = Rx_data_counter; //设置dma传输数据的数量
  40.       if((USART3->SR & (1<<7))) //发送数据寄存器空
  41.       {
  42.         USART3->CR3 &= ~(1<<7);//除能usartdma发送
  43.         USART_DMA_Completed = 0;
  44.         DMA1_Stream3->NDTR = Rx_data_counter; //设置dma传输数据的数量
  45.         DMA1_Stream3->CR |= 1;//使能dma
  46.         USART3->CR3 |= (1<<7);//使能usartdma发送
  47.         Rx_Completed = 0;
  48.         Rx_data_counter = 0;
  49.       }
  50.     }
  51.   }
  52. }

  53. /****************************************
  54.   函数名:USART3_DMA_config
  55.   参数:无
  56.   返回值:无
  57.   功能:DMA1数据流3与usart3关联的相关配置
  58. ****************************************/
  59. void USART3_DMA_config(void)
  60. {
  61.   DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
  62.   while(DMA1_Stream3->CR & 0x00000001);//确保DMA可以被设置
  63.   
  64.   DMA1->LIFCR |= 0x0f800000;//传送前清空DMA1_Stream3所有中断标志
  65.   
  66.   DMA1_Stream3->PAR = (uint32_t)&USART3->DR;//设置外设地址USART3->DR地址0x40004804
  67.   DMA1_Stream3->M0AR = (uint32_t)usart3_buffer; //设置内存地址
  68.   DMA1_Stream3->NDTR = Rx_data_counter; //设置dma传输数据的数量
  69.   //DMA1_Stream3->FCR |= 0x00000007;//设置fifo
  70.   /*
  71.     设置dma通道4,即usart3tx
  72.     优先级Medium
  73.     传输方向内存到外设
  74.     内存递增模式
  75.     传输完成中断使能
  76.   */
  77.   DMA1_Stream3->CR |= ( 0x08000000 |0x00010000 | (1<<6)
  78.                         | (1<<10) | (1<<4) );

  79.   USART3->CR3 &= ~(1<<7);//usart3 dma发送模式除能
  80.   
  81.   NVIC->IP[14] = 0xA0;
  82.   NVIC->ISER[0] |= (1<<14);
  83.   
  84. }

  85. /**************************
  86.   函数名:USART3_config
  87.   参数:无
  88.   返回值:无
  89.   功能:配置usart3
  90. ************************/
  91. void USART3_config(void)
  92. {
  93.   USART3->BRR = 0x0000016C;   //波特率115200
  94.   /*
  95.    使能usart3
  96.   usart3发送使能
  97.   usart3接收使能
  98.   接收缓冲区非空中断使能
  99.   8bit
  100.   一位停止位
  101.   无校验
  102.   */
  103.   USART3->CR1 |= (( 1<<13 ) | ( 1<<3 ) | ( 1<<2 ) | ( 1<<5 ));
  104.   
  105.   GPIOD->AFR[1] |= 0x00000077;//选择PD8,9复用功能
  106.   
  107.   GPIOD->MODER &= 0xFFF0FFFF; //设置PD8,9,复用模式
  108.   GPIOD->MODER |= 0x000A0000;
  109.   
  110. //  GPIOD->OTYPER &= 0xFFFFDFFF; //设置PD9推挽输出
  111.   
  112.   GPIOD->OSPEEDR &= 0xFFFCFFFF; //PD8速度50m
  113.   GPIOD->OSPEEDR |= 0x00020000;
  114.   
  115.   GPIOD->PUPDR &= 0xFFFCFFFF; //PD8
  116.   GPIOD->PUPDR |= 0x00010000;
  117.   
  118.   NVIC->IP[39] = 0xf0; //最低抢占优先级,最低响应优先级1111
  119.   NVIC->ISER[1] |= (1<<(39-32)); //使能中断线39,也就是usart3中断
  120. }


  121. void USART3_IRQHandler(void)
  122. {
  123.   if(USART3->SR & (1<<5)) //接收数据寄存器非空
  124.   {
  125.     usart3_buffer[Rx_data_counter] = USART3->DR;
  126.     Rx_data_counter++;
  127.     if(usart3_buffer[Rx_data_counter - 1] == '\r')
  128.     {
  129.       USART3->CR1 &= ~(1<<5); //除能接收中断
  130.       Rx_Completed = 1;
  131.     }
  132.    }
  133. }

  134. void DMA1_Stream3_IRQHandler(void)
  135. {
  136.   if(DMA1->LISR & 0x08000000)//DMA传输完成
  137.   {
  138.     USART_DMA_Completed = 1;
  139.     USART3->CR1 |= 1<<5;  //使能usart3接收中断
  140.     DMA1->LIFCR |= 0x08000000;//清除中断标志
  141.   }
  142. }
复制代码
运行结果:

9#

奋斗哥

发表于 2013-7-26 10:43:45 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:46 编辑

九、MyDebugger

         已经学习了usart和DMA,因为后面的学习,最好有一个直观点的人性化的显示终端。可以通过串口将数据和文字信息发送往电脑,然后在上位机软件上观察数据以及调试信息。为此,我写了一个文件,以供日后调试之用,命名为MyDebugger。

         首先,参照之前的程序,略加修改,将USART的接收功能全部去掉,DMA的配置分开,独立写成一个配置DMA的函数USART3_DMA_config(),把发送的部分写到MyDebugger_Message(char *str_address, unsigned int str_len)函数内,很简单地实现了一个发送字符消息的函数。然后写一个操作板子上指示灯的函数,用以日后指示调试信息。具体的实现,请看下程序(实现MyDebugger的验证程序)。

         把验证程序分离写成头文件形式。方便以后其他工程使用。把程序稍作修改,利用条件编译,为以后可能的添加其他通信方式提供方便。要使用USART3作为调试通信方式,必须先定义宏MyDebug_with_USART3,如下图所示:



实现MyDebugger的验证程序:

  1. /*********************************************
  2.     标题:MyDebugger
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     描述:实现一个调试工具
  8.          
  9.     author:小船
  10.     data:2012-02-04
  11. **********************************************/
  12. #include <stm32f4xx.h>
  13. #include <stdbool.h>

  14. /******LED宏定义*******/
  15. #define green 0x00001000
  16. #define orange 0x00002000
  17. #define red 0x00004000
  18. #define blue 0x00008000

  19. /******全局变量及类型声明*******/
  20. bool USART_DMA_Completed;
  21. enum LED_State {on, off, turn};

  22. /******函数声明*******/
  23. void LEDs_Init(void);
  24. void USART3_DMA_config(void);
  25. void USART3_config(void);
  26. bool MyDebugger_Message(char *str_address, unsigned int str_len);
  27. void MyDebugger_LEDs(uint32_t LED, enum LED_State state);

  28. void main ()
  29. {
  30.   SysTick_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断
  31.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  32.   LEDs_Init();
  33.   USART3_DMA_config();
  34.   USART3_config();
  35.   
  36.   USART_DMA_Completed = 1;
  37.   
  38.   while(1)
  39.   {
  40.     MyDebugger_Message("My name is Xian Yongwen\n",
  41.                             sizeof("My name is Xian Yongwen\n")/sizeof(char));
  42.    
  43.     MyDebugger_Message("广东石油化工学院\n",
  44.                             sizeof("广东石油化工学院\n")/sizeof(char));
  45.   }
  46. }

  47. /*********************************************
  48.   函数名:MyDebugger_Message
  49.   参数:char *str_address  :要发送的字符串地址
  50.         unsigned int str_len  :字符串的长度
  51.   返回值:bool  是否操作成功
  52.   功能:通过USART3发送信息
  53. **********************************************/
  54. bool MyDebugger_Message(char *str_address, unsigned int str_len)
  55. {
  56.     if( USART_DMA_Completed )  //之前数据已经发送完成
  57.     {  
  58.       DMA1_Stream3->CR &= 0xFFFFFFFE;   //除能DMA1_Stream3
  59.       while(DMA1_Stream3->CR & 0x00000001); //确保DMA可以被设置     
  60.       DMA1->LIFCR |= 0x0f800000;  //传送前清空DMA1_Stream3所有中断标志  
  61.       DMA1_Stream3->M0AR = (uint32_t)str_address; //设置内存地址
  62.       if((USART3->SR & (1<<7))) //发送数据寄存器空
  63.       {
  64.         USART3->CR3 &= ~(1<<7);//usart3 dma发送模式除能
  65.         USART_DMA_Completed = 0;
  66.         DMA1_Stream3->NDTR = str_len; //设置dma传输数据的数量
  67.         DMA1_Stream3->CR |= 1;//使能dma
  68.         USART3->CR3 |= (1<<7);//usart3 dma发送模式使能
  69.         return true;
  70.       }
  71.     }
  72.     return false;
  73. }

  74. /****************************************
  75.   函数名:MyDebugger_LEDs
  76.   参数:uint32_t LED  :要操作哪些LED
  77.         enum LED_State state  :作何操作
  78.   返回值:无
  79.   功能:改变LED的状态
  80. ****************************************/
  81. void MyDebugger_LEDs(uint32_t LED, enum LED_State state)
  82. {
  83.   uint32_t tmp;
  84.   switch (state)
  85.   {
  86.     case on:
  87.       {
  88.         GPIOD->BSRRL |= LED;
  89.         break;
  90.       }
  91.     case off:
  92.       {
  93.         GPIOD->BSRRH |= LED;
  94.         break;
  95.       }
  96.     case turn:
  97.       {
  98.         tmp = (~GPIOD->ODR) & LED;   
  99.         GPIOD->ODR &= ~LED;
  100.         GPIOD->ODR |= tmp;
  101.         break;
  102.       }
  103.   }
  104. }

  105. /****************************************
  106.   函数名:USART3_DMA_config
  107.   参数:无
  108.   返回值:无
  109.   功能:DMA1数据流3与usart3关联的相关配置
  110. ****************************************/
  111. void USART3_DMA_config(void)
  112. {
  113.   RCC->AHB1ENR |= (1<<21); //使能DMA1时钟
  114.   DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
  115.   while(DMA1_Stream3->CR & 0x00000001);//确保DMA可以被设置
  116.   
  117.   DMA1->LIFCR |= 0x0f800000;//传送前清空DMA1_Stream3所有中断标志
  118.   
  119.   DMA1_Stream3->PAR = (uint32_t)&USART3->DR;//设置外设地址USART3->DR地址0x40004804
  120.                                              //设置内存地址
  121.   DMA1_Stream3->FCR &= 0x00000000;
  122.   DMA1_Stream3->FCR |= (1<<7);//设置fifo
  123.   /*
  124.     设置dma通道4,即usart3tx
  125.     优先级最高
  126.     传输方向内存到外设
  127.     内存递增模式
  128.     传输完成中断使能
  129.   */
  130.   DMA1_Stream3->CR |= (0x08000000 | 0x00030000 | (1<<6)
  131.                        | (1<<10) | (1<<4) | (1<<2)|(1<<1));
  132.   
  133.   NVIC->IP[14] = 0xA0;
  134.   NVIC->ISER[0] |= (1<<14);
  135.   
  136. }

  137. /**************************
  138.   函数名:USART3_config
  139.   参数:无
  140.   返回值:无
  141.   功能:配置usart3
  142. ************************/
  143. void USART3_config(void)
  144. {
  145.   RCC->APB1ENR |= (1<<18);  //使能usart3时钟
  146.   RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟
  147.   USART3->BRR = 0x0000016C;   //波特率115200
  148.   /*
  149.    使能usart3
  150.   usart3发送使能
  151.   8bit
  152.   一位停止位
  153.   无校验
  154.   */
  155.   USART3->CR1 |= ( ( 1<<13 ) | ( 1<<3 ) );
  156.   
  157.   GPIOD->AFR[1] |= 0x00000077;//选择PD8,9复用功能
  158.   
  159.   GPIOD->MODER &= 0xFFF0FFFF; //设置PD8,9,复用模式
  160.   GPIOD->MODER |= 0x000A0000;
  161.    
  162.   GPIOD->OSPEEDR &= 0xFFFCFFFF; //PD8速度50m
  163.   GPIOD->OSPEEDR |= 0x00020000;
  164.   
  165.   GPIOD->PUPDR &= 0xFFFCFFFF; //PD8
  166.   GPIOD->PUPDR |= 0x00010000;
  167. }

  168. /****************************************
  169.   函数名:LEDs_Init
  170.   参数:无
  171.   返回值:无
  172.   功能:初始化板子上的LED
  173. ****************************************/
  174. void LEDs_Init(void)
  175. {
  176.   RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟
  177.   
  178.   GPIOD->MODER &= 0x00FFFFFF; //设置PD12,13,14,15输出
  179.   GPIOD->MODER |= 0x55000000;
  180.   
  181.   GPIOD->OTYPER &= 0xFFFF0FFF; //设置PD12,13,14,15推挽输出
  182.   
  183.   GPIOD->OSPEEDR &= 0x00FFFFFF; //PD12,13,14,15 速度100m
  184.   
  185.   GPIOD->PUPDR &= 0x00FFFFFF;  //PD12,13,14,15 无上拉无下拉
  186.   
  187.   GPIOD->BSRRH = 0xf000;  //reset register GPIOx_BSRRH, write only
  188.                           //set register GPIOx_BSRRL, write only
  189. }

  190. void DMA1_Stream3_IRQHandler(void)
  191. {
  192.   if(DMA1->LISR & 0x08000000)//DMA传输完成
  193.   {
  194.     USART_DMA_Completed = 1;
  195.     DMA1->LIFCR |= 0x08000000;//清除中断标志
  196.   }
  197.   if(DMA1->LISR & 0x03000000)     //如果发生传输错误或直接模式错误,亮橙色LED
  198.   {
  199.     MyDebugger_LEDs( orange, on);
  200.     DMA1->LIFCR |= 0x03000000;
  201.   }
  202.   if(DMA1->LISR & (1<<22))  //如果发生fifo错误,亮红色指示灯
  203.   {
  204.     MyDebugger_LEDs( red, on);
  205.     DMA1->LIFCR |= (1<<22);
  206.   }
  207. }
复制代码

实用程序:


头文件:MyDebugger.h

  1. <p>// file:MyDebugger.h</p>#ifndef __MyDebugger_H
  2. #define __MyDebugger_H

  3. #include <stm32f4xx.h>
  4. #include <stdbool.h>

  5. /******LED宏定义*******/
  6. #define green 0x00001000
  7. #define orange 0x00002000
  8. #define red 0x00004000
  9. #define blue 0x00008000

  10. /******全局变量及类型声明*******/
  11. enum LED_State {on, off, turn};

  12. /******函数声明*******/
  13. void MyDebugger_Init(void);
  14. bool MyDebugger_Message(char *str_address, unsigned int str_len);
  15. void MyDebugger_LEDs(uint32_t LED, enum LED_State state);

  16. #endif
复制代码

源文件:MyDebugger.c

  1. // file:MyDebugger.c
  2. #include <MyDebugger.h>

  3. #ifdef MyDebug_with_USART3

  4. bool USART_DMA_Completed = true;

  5. /****************************************
  6.   函数名:USART3_DMA_config
  7.   参数:无
  8.   返回值:无
  9.   功能:DMA1数据流3与usart3关联的相关配置
  10. ****************************************/
  11. void USART3_DMA_config(void)
  12. {
  13.   RCC->AHB1ENR |= (1<<21); //使能DMA1时钟
  14.   DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
  15.   while(DMA1_Stream3->CR & 0x00000001);//确保DMA可以被设置
  16.   
  17.   DMA1->LIFCR |= 0x0f800000;//传送前清空DMA1_Stream3所有中断标志
  18.   
  19.   DMA1_Stream3->PAR = (uint32_t)&USART3->DR;//设置外设地址USART3->DR地址0x40004804
  20.                                              //设置内存地址
  21.   DMA1_Stream3->FCR &= 0x00000000;
  22.   DMA1_Stream3->FCR |= (1<<7);//设置fifo
  23.   /*
  24.     设置dma通道4,即usart3tx
  25.     优先级最高
  26.     传输方向内存到外设
  27.     内存递增模式
  28.     传输完成中断使能
  29.   */
  30.   DMA1_Stream3->CR |= (0x08000000 | 0x00030000 |
  31.                         (1<<6) | (1<<10) | (1<<4));
  32.   
  33.   NVIC->IP[14] = 0xA0;
  34.   NVIC->ISER[0] |= (1<<14);
  35. }

  36. /**************************
  37.   函数名:USART3_config
  38.   参数:无
  39.   返回值:无
  40.   功能:配置usart3
  41. ************************/
  42. void USART3_config(void)
  43. {
  44.   RCC->APB1ENR |= (1<<18);  //使能usart3时钟
  45.   RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟
  46.   USART3->BRR = 0x0000016C;   //波特率115200
  47.   /*
  48.    使能usart3
  49.   usart3发送使能
  50.   8bit
  51.   一位停止位
  52.   无校验
  53.   */
  54.   USART3->CR1 |= ( ( 1<<13 ) | ( 1<<3 ) );
  55.   
  56.   GPIOD->AFR[1] |= 0x00000077;//选择PD8,9复用功能
  57.   
  58.   GPIOD->MODER &= 0xFFF0FFFF; //设置PD8,9,复用模式
  59.   GPIOD->MODER |= 0x000A0000;
  60.   
  61. //  GPIOD->OTYPER &= 0xFFFFDFFF; //设置PD9推挽输出
  62.   
  63.   GPIOD->OSPEEDR &= 0xFFFCFFFF; //PD8速度50m
  64.   GPIOD->OSPEEDR |= 0x00020000;
  65.   
  66.   GPIOD->PUPDR &= 0xFFFCFFFF; //PD8
  67.   GPIOD->PUPDR |= 0x00010000;
  68. }

  69. void DMA1_Stream3_IRQHandler(void)
  70. {
  71.   if(DMA1->LISR & 0x08000000)//DMA传输完成
  72.   {
  73.     USART_DMA_Completed = 1;
  74.     DMA1->LIFCR |= 0x08000000;//清除中断标志
  75.   }
  76. }
  77. #endif

  78. /*********************************************
  79.   函数名:MyDebugger_Message
  80.   参数:char *str_address  :要发送的字符串地址
  81.         unsigned int str_len  :字符串的长度
  82.   返回值:bool  是否操作成功
  83.   功能:通过USART3发送信息
  84. **********************************************/
  85. bool MyDebugger_Message(char *str_address, unsigned int str_len)
  86. {   
  87. #ifdef MyDebug_with_USB
  88.     bool USB_Actioned;
  89. #endif
  90.    
  91. #ifdef MyDebug_with_USART3
  92.     bool USART3_Actioned;
  93.     if( USART_DMA_Completed )  //之前数据已经发送完成
  94.     {  
  95.       DMA1_Stream3->CR &= 0xFFFFFFFE;   //除能DMA1_Stream3
  96.       while(DMA1_Stream3->CR & 0x00000001); //确保DMA可以被设置     
  97.       DMA1->LIFCR |= 0x0f800000;  //传送前清空DMA1_Stream3所有中断标志  
  98.       DMA1_Stream3->M0AR = (uint32_t)str_address; //设置内存地址
  99.       if((USART3->SR & (1<<7))) //发送数据寄存器空
  100.       {
  101.         USART3->CR3 &= ~(1<<7);//usart3 dma发送模式除能
  102.         USART_DMA_Completed = 0;
  103.         DMA1_Stream3->NDTR = str_len; //设置dma传输数据的数量
  104.         DMA1_Stream3->CR |= 1;//使能dma
  105.         USART3->CR3 |= (1<<7);//usart3 dma发送模式使能
  106.         USART3_Actioned = 1;
  107.       }
  108.     }
  109. #endif
  110.    
  111. #ifdef MyDebug_with_USB
  112. //以后或许实现用USB发送调试信息的代码
  113.    
  114.        //如果操作USB成功
  115.         USB_Actioned = 1;
  116. #endif

  117. #ifdef MyDebug_with_USART3
  118.   #ifndef MyDebug_with_USB   
  119.     return USART3_Actioned;
  120.   #endif  
  121. #endif

  122. #ifdef MyDebug_with_USB
  123.   #ifndef MyDebug_with_USART3   
  124.     return USB_Actioned;
  125.   #endif  
  126. #endif

  127. #ifdef MyDebug_with_USART3
  128.   #ifdef MyDebug_with_USB   
  129.     return (USART3_Actioned | USB_Actioned);
  130.   #endif
  131. #endif   
  132. }

  133. /****************************************
  134.   函数名:LEDs_Init
  135.   参数:无
  136.   返回值:无
  137.   功能:初始化板子上的LED
  138. ****************************************/
  139. void LEDs_Init(void)
  140. {
  141.   RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟
  142.   
  143.   GPIOD->MODER &= 0x00FFFFFF; //设置PD12,13,14,15输出
  144.   GPIOD->MODER |= 0x55000000;
  145.   
  146.   GPIOD->OTYPER &= 0xFFFF0FFF; //设置PD12,13,14,15推挽输出
  147.   
  148.   GPIOD->OSPEEDR &= 0x00FFFFFF; //PD12,13,14,15 速度100m
  149.   
  150.   GPIOD->PUPDR &= 0x00FFFFFF;  //PD12,13,14,15 无上拉无下拉
  151.   
  152.   GPIOD->BSRRH = 0xf000;  //reset register GPIOx_BSRRH, write only
  153.                           //set register GPIOx_BSRRL, write only
  154. }

  155. /****************************************
  156.   函数名:MyDebugger_LEDs
  157.   参数:uint32_t LED  :要操作哪些LED
  158.         enum LED_State state  :作何操作
  159.   返回值:无
  160.   功能:改变LED的状态
  161. ****************************************/
  162. void MyDebugger_LEDs(uint32_t LED, enum LED_State state)
  163. {
  164.   uint32_t tmp;
  165.   switch (state)
  166.   {
  167.     case on:
  168.       {
  169.         GPIOD->BSRRL |= LED;
  170.         break;
  171.       }
  172.     case off:
  173.       {
  174.         GPIOD->BSRRH |= LED;
  175.         break;
  176.       }
  177.     case turn:
  178.       {
  179.         tmp = (~GPIOD->ODR) & LED;   
  180.         GPIOD->ODR &= ~LED;
  181.         GPIOD->ODR |= tmp;
  182.         break;
  183.       }
  184.   }
  185. }

  186. /*********************************************
  187.   函数名:MyDebugger_Init
  188.   参数:无
  189.   返回值:无
  190.   功能:初始化MyDebugger
  191. **********************************************/
  192. void MyDebugger_Init(void)
  193. {
  194.   LEDs_Init();
  195.   
  196. #ifdef MyDebug_with_USART3
  197.   USART3_DMA_config();
  198.   USART3_config();
  199. #endif
  200. }
复制代码

主程序文件:main.c

  1. /*********************************************
  2.     标题:MyDebugger
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     描述:实现一个调试工具
  8.          
  9.     author:小船
  10.     data:2012-02-04
  11. **********************************************/
  12. #include <stm32f4xx.h>
  13. #include <MyDebugger.h>

  14. uint32_t Gb_TimingDelay;

  15. void Delay(uint32_t nTime);

  16. void main ()
  17. {
  18.   SysTick_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断
  19.   //char test[15] = {0, 1, 2, 3, 4,  10, 11, 12, 13, 14, 15, 0xfe, 0xf0, 0x07, 0x09};
  20.   SCB->AIRCR = 0x05AF0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  21.   MyDebugger_Init();
  22.   while(1)
  23.   {
  24.     MyDebugger_Message("My name is Xian Yongwen\n\r",
  25.                             sizeof("My name is Xian Yongwen\n\r")/sizeof(char));
  26.    
  27.     MyDebugger_LEDs(blue, on);
  28.     Delay(500);
  29.    
  30.     MyDebugger_Message("广东石油化工学院\n\r",
  31.                             sizeof("广东石油化工学院\n\r")/sizeof(char));
  32.    
  33.     MyDebugger_LEDs(blue, off);
  34.     Delay(500);
  35.   }
  36. }

  37. void Delay(uint32_t nTime)
  38. {
  39.   Gb_TimingDelay = nTime;

  40.   while(Gb_TimingDelay != 0);
  41. }

  42. void SysTick_Handler(void)
  43. {
  44.   if (Gb_TimingDelay != 0x00)
  45.   {
  46.     Gb_TimingDelay--;
  47.   }
  48. }
复制代码

运行结果:

10#

奋斗哥

发表于 2013-7-26 10:44:20 | 只看该作者

本帖最后由 奋斗哥 于 2013-7-26 13:47 编辑

基本定时器TIM6&TIM7



      基本定时器TIM6和TIM7包括一个由可编程分频器驱动的16位自动重载计数器。它可以用作普通的定时器,也可以用来驱动DAC。这两个定时器是完全独立的,不共享任何资源。更多时候是与DAC平配合使用。

作为定时器的配置步骤:

1.      设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

2.     使能相关时钟。

3.     设置分频。

4.     清空计数器的值。

5.     设置自动重装寄存器的值。

6.     是否允许中断。

7.     如果允许中断,设置中断优先级,使能中断。

8.     使能计数器。

程序:

  1. /************************************
  2.     标题:定时器TIM7的定时练习
  3.     软件平台:IAR for ARM6.21
  4.     硬件平台:stm32f4-discovery
  5.     主频:168M
  6.    
  7.     author:小船
  8.     data:2012-02-06
  9. *************************************/

  10. #include <stm32f4xx.h>
  11. #include "MyDebugger.h"

  12. void main ()
  13. {   

  14.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
  15.   
  16.   RCC->APB1ENR |= (1<<5); //打开TIM7时钟
  17.   TIM7->PSC = 8399; //对时钟84M进行8400分频,使得计数频率为10k
  18.   TIM7->ARR = 10000;  //定时一秒
  19.   TIM7->CNT = 0;  //清空计数器
  20.   TIM7->CR1 |= (1<<7); //自动重装载预装载使能
  21.   TIM7->DIER |= 1; //使能中断
  22.   NVIC->IP[55] = 0x80;
  23.   NVIC->ISER[1] |= (1<<(55-32));
  24.   TIM7->CR1 |= 1; //开始计时
  25.   MyDebugger_Init();
  26.   while(1)
  27.   {
  28.   };
  29. }

  30. void TIM7_IRQHandler(void)
  31. {
  32.   if(TIM7->SR)
  33.   {
  34.     MyDebugger_LEDs(blue, turn);
  35.     TIM7->SR &= ~(0x0001);
  36.   }
  37. }
复制代码
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条