打印
[工具和软件]

从零入手Kinetis系统开发之eDMA模块

[复制链接]
3697|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

言归正传,上次说过要写篇DMA的介绍来,结果还是耽误了,这次就补上了,另外这里预告一下,下篇的从零入手打算写一下FlexBus的软件部分(前面说了说它的硬件设计),如果有用到的话敬请期待,不会用到或者早就已经熟用了就不用等了,当然期间还会穿插一些其他的工程技巧的,不要错过哦,亲...(咳咳,借用下淘宝体)下面就正式介绍下Kinetis的DMA:

1.先介绍下DMA。DMA(Direct Memory Access)是一种直接存储器访问技术,工作过程中不需要CPU干预,也不需要像中断处理方式那样需要保留现场、恢复现场之类的麻烦事,简单理解为一条直接连通外设与RAM的硬件通道(这句话需要仔细琢磨,下面**里会重点提一下),所以DMA技术可以提高系统运行效率(即CPU可以干其他的事去,算是一种简单的并行模式吧)。而对Kinetis来说,我们会看到eDMA,其实就是多了个e(enhanced,即增强型),嘿嘿,至于为什么会多了个e,从下面对Kinetis的eDMA特性的分析就可以看出来;

2.Kinetis的eDMA包括两部分,即DMA Muliplexer(DMA多路转换器,就是个矩阵开关,路由DMA触发源的)和DMA Controller(DMA控制器,这个是重点,用来配置DMA控制引擎(Engine)和DMA传输控制描述区(TCD)),其内部框图如下所示:

DMAMUX框图

DMA Controller框图

3.下面介绍下Kinetis的eDMA的一些特性,有点多,就挑重点和特色的来说了:

(1)16个独立可配置的DMA通道,其中前四个通道可配置成周期性触发(需要用到PIT模块),如上图DMAMUX框图所示;

(2)52个外设触发slots(这个我担心翻译不好误人子弟了就直接用该单词替代了,呵呵,用过Qt的人都这是个槽的概念,大家权当触发源来理解吧),10个直通slots,每一个slot可以通过软件编程路由到16个DMA通道中的任意一个(这个通过配置DMAMUX_CONFIGn得到),这方面比STM32的M3好些,STM32是固定的,所以Kinetis灵活性好些(咳咳,该言论不代表贬低STM32,毕竟它还是很强大的);

(3)独立可编程的源地址、目标地址和传输宽度(8bit,16bit,32bit,另外支持16byte的缓存),支持外设到RAM,RAM到外设,RAM到RAM之间的传输;

(4)每一个通道都有一个11个寄存器的TCD(Tranfer control descripter),注意这11个寄存器(包括16位和32位宽度的寄存器)才是我们编写驱动的重点对象;

(5)固定的优先级模式和时间轮询(round-robin)优先级模式(注意:如果不通过软件设置优先级的话,系统默认为每个通道的优先级等于它的通道号,即0通道的优先级为0,且优先级号越小,其优先级越低);

(6)每个通道包括了三个中断标志,即DMA半传输完成标志、DMA传输完成标志和DMA传输出错标志,3个标志逻辑或成一个中断请求(所以如果都使能了,那可以通过查询相关标志寄存器来判断当前的中断类型);

(7)可软件中断取消DMA传输(通过配置DMA_CR_CX位)。

4.DMA工作流程,这里先给出清华的一个功能框图,我觉着还是挺清晰的,便于理解,然后接下来的三幅图分别代表了一个DMA完整传输的流程,这里一句两句没法说清楚就只能上图了,不懂的可以下面留言,呵呵:

整体框图

part1

part2

part3

5.在具体软件编程之前,先说下次循环(minor loop)和主循环(major loop)这两个概念,用图来表达我觉着好些,上图,呼呼:


相关帖子

沙发
我思故我在12345|  楼主 | 2015-4-23 21:39 | 只看该作者
6.软件编程,前面做了那么多的准备,就是为了这一步能轻松的编写Kinetis的DMA驱动,我通过一个实例说下DMA的软件编写方法(嘿嘿,其实大家都喜欢看例子),该例子实现的功能是实现放在RAM区的一块数据(自己定义一个数组)通过DMA方式传输到UART,然后通过超级终端接收实际数据,挺好玩的吧,下面就实现它:

(1)首先为使UART支持DMA中断请求,需要在UART的初始化函数里加入下面三行代码(就不另介绍UART的初始化函数了,这里用的就是我曾经上传的开源开发框架代码里的UART函数):



    /* 以下为使能UART的DMA功能 */  
    UART_C2_REG(uartch) |= UART_C2_TIE_MASK;      /* 使能UART发送中断或者DMA请求 */
    UART_C2_REG(uartch) &= ~UART_C2_TCIE_MASK;    /* 禁止发送中断,只使能DMA请求*/
    UART_C5_REG(uartch) |= UART_C5_TDMAS_MASK;    /* 打开UART发送的DMA请求 */

(2)这一步完成之后就需要自己编写DMA的配置函数了,废话不多说,直接上代码了,由于程序里我已经加上了足够详细的著述了所以就不多做解释了,呵呵:
/*******************************************************************************
**File Name: DMA.c
**Description: The Driver of DMA module of Kinetis
**Editor: jicheng at Shandong University
**Date: 2012.05.02
**History: V1.0
**Notes: 无
*******************************************************************************/
#include "Kinetis_Config.h"
#include "DMA.h"
/********************************************************************************
**Routine: myDMA_Config
**Description: DMA配置函数
               DMA_CHn——指定的DMA通道号,范围0~15;
               DMAMUX_Source——DMA触发源,在DMA.h文件里有枚举定义
               S_Addr——DMA传送源地址
               D_Addr——DMA传送目的地址
               Block_Size——一次DMA传输的数据块大小(/byte)
**Notes: 默认情况下,固定优先级模式,每个有通道分配的优先级等于该通道的通道号,
        所以通道号小的优先级低。(本例程默认优先级配置)
********************************************************************************/
void myDMA_Config(uint8 DMA_CHn, uint8 DMAMUX_Source, uint32 S_Addr, uint32 D_Addr, uint16 Block_Size)
{  
  /* the corresponding SIM clock gate to be enabled befor the DMAMUX module
     register being initialized */
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
  /* Config DMAMUX for channel n */
  DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR, DMA_CHn) = (0
                                                | DMAMUX_CHCFG_ENBL_MASK              /* 使能DMA通道 */
                                                //| DMAMUX_CHCFG_TRIG_MASK              /* 打开周期性触发模式,注意只有0~3通道支持 */
                                                | DMAMUX_CHCFG_SOURCE(DMAMUX_Source)  /* 指定DMA触发源 */
                                               );
   
  /* enable the DMA clock gate in SIM */
  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;                            /* DMA时钟门控上电默认是打开的,所以这步可加可不加 */
  DMA_CR = 0;                                                 /* 默认配置,需要在DMA被激活之前配置此寄存器 */
//DMA_DCHPRIn                                                 /* 默认优先级配置,这里不另更改 */
  DMA_BASE_PTR->TCD[DMA_CHn].SADDR = S_Addr;                  /* 分配DMA源地址 */
  DMA_BASE_PTR->TCD[DMA_CHn].DADDR = D_Addr;                  /* 分配DMA目标地址 */
  DMA_BASE_PTR->TCD[DMA_CHn].NBYTES_MLNO = 1;                 /* 每次minor loop传送1个字节 */
  DMA_BASE_PTR->TCD[DMA_CHn].ATTR  =(0
                                     |DMA_ATTR_SMOD(0)        /* Source modulo feature disabled */
                                     | DMA_ATTR_SSIZE(0)      /* Source size, 8位传送 */
                                     | DMA_ATTR_DMOD(0)       /* Destination modulo feature disabled */
                                     | DMA_ATTR_DSIZE(0)      /* Destination size, 8位传送 */
                                    );
  DMA_BASE_PTR->TCD[DMA_CHn].SOFF  = 0x0001;                  /* 每次操作完源地址,源地址增加1 */
  DMA_BASE_PTR->TCD[DMA_CHn].DOFF  = 0x0000;                  /* 每次操作完目标地址,目标地址不增加  */
  DMA_BASE_PTR->TCD[DMA_CHn].SLAST = 0x00;                    /* DMA完成一次输出之后即major_loop衰减完之后不更改源地址 */
  DMA_BASE_PTR->TCD[DMA_CHn].DLAST_SGA = 0x00;                /* DMA完成一次输出之后即major_loop衰减完之后不更改目标地址 */
  DMA_BASE_PTR->TCD[DMA_CHn].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(Block_Size); /* 1个major loop, 即一次传输量=major_loop*minor_loop,最大为2^15=32767 */
  DMA_BASE_PTR->TCD[DMA_CHn].BITER_ELINKNO = DMA_CITER_ELINKNO_CITER(Block_Size); /* BITER应该等于CITER */
  
  DMA_BASE_PTR->TCD[DMA_CHn].CSR = 0;                         /* 先清零CSR,之后再设置 */
  DMA_INT &=~(1<<DMA_CHn);                                    /* 关闭DMA相应通道的传输完成中断,与TCD_CSR_INTMAJOR或者TCD_CSR_INTHALF搭配 */
  DMA_BASE_PTR->TCD[DMA_CHn].CSR &= ~DMA_CSR_INTMAJOR_MASK;   /* 关闭DMA major_loop完成中断 */
  DMA_BASE_PTR->TCD[DMA_CHn].CSR |= DMA_CSR_DREQ_MASK;        /* major_loop递减为0时自动关闭DMA,即只进行一次DMA传输 */
  
  /* DMA_ERQ寄存器很重要,置位相应的位即开启DMA工作 */
  DMA_ERQ &= ~(1 << DMA_CHn);                                 /* 关闭相应通道的DMA请求,在配置阶段先关闭,再调用myDMA_Start函数开启DMA */
}
/********************************************************************************
**Routine: myDMA_Start
**Description: 开启DMA请求,使能DMA工作
**Notes: 无
********************************************************************************/
void myDMA_Start(uint8 DMA_CHn)
{
  DMA_ERQ |= (1 << DMA_CHn);                                  /* 开启相应通道的DMA */
}
/********************************************************************************
**Routine: myDMA_Close
**Description: 关闭相应通道的DMA请求,停止DMA工作
**Notes:
********************************************************************************/
void myDMA_Close(uint8 DMA_CHn)
{
  DMA_ERQ &= ~(1 << DMA_CHn);                                 /* 停止相应通道的DMA */
}

(3)这里需要强调的是,触发源,下图为部分触发源安排,这些我在头文件里进行了枚举定义:


(4)一切准备就绪,下面看看再application里的实际调用吧,嘿嘿,如下:
uint8 DataToSend[]={   /* 此处数组定义必须定义在RAM里,定义成const放在flash里是不行的,容易跟CPU抢flash总线 */
                      "The DMA test data is transfering...\r\n"
                      "\r\n"
                      "Congratulations! The transport is completed successfully!\r\n"
                      "\r\n"  
                      "                           jicheng at shandong university\r\n"
                      "                                               2012.5.16"
                     };
void main(void)
{
  //---------------insert your code in the following
  /* 初始化外设UART */
  UART_Init(UART3,(Kinetis_CORE_CLK/Kinetis_DIV_BusClk)*1000,9600);
  /* 初始化配置DMA */
  myDMA_Config(1,DMA_UART3_T,(uint32)DataToSend,0x4006D007,sizeof(DataToSend));
  /* 启动DMA传送 */
  myDMA_Start(1);

  EnableInterrupts;
  
  while(1)
  {

  }
  
}

使用特权

评论回复
板凳
我思故我在12345|  楼主 | 2015-4-23 21:39 | 只看该作者
(5)嘿嘿,下面见效果图,呼呼:



使用特权

评论回复
地板
我思故我在12345|  楼主 | 2015-4-23 21:40 | 只看该作者
附件为完整的DMA驱动文件(.c和.h)~
6404147119020.rar (2.31 KB)

使用特权

评论回复
5
fhguo1990| | 2015-4-23 21:41 | 只看该作者
感谢楼主,作为新手,我真的是受益匪浅,不过,我在使用楼主的这些代码的时候有几个小问题.
1.使用这些驱动文件的话,必须要用楼主提供的那个开发框架吗?
2.楼主曾经提供了一个非常好的开发框架,但楼主使用的调试是JLINK调试,而我这边的调试是基于OSJTAG的,就是那个PE,然后,我悲剧的发现框架里面好像将OSJTAG相关的串口设置全去掉了.不过,我非常喜欢你的那个框架,所以想知道应该怎么修改.

使用特权

评论回复
6
我思故我在12345|  楼主 | 2015-4-23 21:42 | 只看该作者
fhguo1990 发表于 2015-4-23 21:41
感谢楼主,作为新手,我真的是受益匪浅,不过,我在使用楼主的这些代码的时候有几个小问题.
1.使用这些驱动文件 ...

不一定非要用,我写的这个就是个通用的驱动接口,稍微改一下就可以移植到其他的工程里;另外我那个框架程序也可以用OSJTAG下载的,就是串口的话没用官方自带的(官方的需要用到那个PE的串口终端);如果你想修改的话可以看看我的那篇详解开发框架博客,有问题在下面留言即可~

使用特权

评论回复
7
fhguo1990| | 2015-4-23 21:42 | 只看该作者
myDMA_Config(1,DMA_UART3_T,(uint32)DataToSend,0x4006D007,sizeof(DataToSend)); 这个函数中 0x4006D007是什么意思?

使用特权

评论回复
8
fhguo1990| | 2015-4-23 21:43 | 只看该作者
  • 哦,找到了,是不是这个?这个是不是串口发送的寄存器?相当于51中的SBUF寄存器?

使用特权

评论回复
9
我思故我在12345|  楼主 | 2015-4-23 21:44 | 只看该作者
fhguo1990 发表于 2015-4-23 21:43
  • 哦,找到了,是不是这个?这个是不是串口发送的寄存器?相当于51中的SBUF寄存器?
    ...

  • 嗯,是的其实可以用&UART3_D来代替,忘记改了,呵呵。对的,是你所说的串口发送的寄存器~

    使用特权

    评论回复
    10
    fhguo1990| | 2015-4-23 21:44 | 只看该作者
    学习了~
    不过有一个问题,是每次DMA传输前都要进行这些配置,还是配置好了之后,下次传输只需要执行这几句话呢:
      DMA_BASE_PTR->TCD[DMA_CHn].SADDR = S_Addr;                  /* 分配DMA源地址 */
      DMA_BASE_PTR->TCD[DMA_CHn].DADDR = D_Addr;                  /* 分配DMA目标地址 */
      DMA_ERQ &= ~(1 << DMA_CHn);      
      DMA_ERQ |= (1 << DMA_CHn);

    使用特权

    评论回复
    11
    我思故我在12345|  楼主 | 2015-4-23 21:45 | 只看该作者
    fhguo1990 发表于 2015-4-23 21:44
    学习了~
    不过有一个问题,是每次DMA传输前都要进行这些配置,还是配置好了之后,下次传输只需要执行这几句 ...

    我设置的只是一次传输,可以通过设置寄存器让DMA连续传输,见寄存器DMA_CSR_DREQ说明~

    使用特权

    评论回复
    12
    fhguo1990| | 2015-4-23 21:45 | 只看该作者
    你好,一直在学习你的K60的学习开发,最近在DMA上遇到了问题!请求指导....以下是数字摄像头程序
    #include "MK60N512VMD100.h"  //单片机头文件
    #include "common.h"
    /*------------------------------------
    //使用PTC8 PTC9 PTC10 PTC11 PTC12 PTC13 PTC14  PTC15 采集数字摄像头OV7620 8位灰度输入
    //E24引脚设置为GPIO模式,下降沿中断,场中断
    //A8引脚设置为GPIO模式,上升沿中断,行中断
    //PTC0引脚设置为GPIO模式,用软件PWM产生2.5M的触发脉冲作为上升沿触发DMA请求
    //共采集27行,每行160个点,dma传送到video数组

    *********************************************************/
    //----------
    #define PIN(x)  (1 << x)
    #define BUS_CLOCK  100  //(MHZ)50 82 90 100 105 110 115
    #define BAUD 19200     //波特率
    #define endlineROW 240        //OV7620,每场240行
    #define  H 27
    #define  V 160
    unsigned int row=0;//摄像头行计数,最大240
    uint8 video[H][V];//存放数据数组
    unsigned int imagerow=0;//采集行计数,最大H
    unsigned int const data_table[H]={46,53,68,83,97,109,120,130,139,147,
                                      155,163,170,178,186,192,197,202,206,210,
                                      214,218,222,225,228,231,234};//需采集数据的行,2cm一行实际测得
    uint8 row_F[H];//该行采集完成标志
    char startline;//起始行
    char endline;//结束行
    char startline_F;//发现起始行
    char endline_F;//发现结束行
    uint32 temp_pwm1 = 2000;
    uint32 speed_PWM_R2 = 1500;
    uint32 servo_PWM_R = 1250;
    uint32 temp_pwm4 = 15;
    uint32 temp_pwm5 = 5000;
    uint32 temp_pwm6 = 20;
    uint32 dma0 = 0;

    uint16 inttrrupt_count=0;
    /****************************初始化开始****************************/
    //----------------------PLL锁相环初始化----------------------------//
    void PLL_Init(void)
    {
        uint32_t temp_reg;
        //这里处在默认的FEI模式
        //首先移动到FBE模式
        MCG_C2 = 0;
        //MCG_C2 = MCG_C2_RANGE(2) | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK;
        //初始化晶振后释放锁定状态的振荡器和GPIO
        SIM_SCGC4 |= SIM_SCGC4_LLWU_MASK;
        LLWU_CS |= LLWU_CS_ACKISO_MASK;
        //选择外部晶振,参考分频器,清IREFS来启动外部晶振
        //011 If RANGE = 0, Divide Factor is 8; for all other RANGE values, Divide Factor is 256.
        MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);
        //等待晶振稳定
        //while (!(MCG_S & MCG_S_OSCINIT_MASK)){}              //等待锁相环初始化结束
        while (MCG_S & MCG_S_IREFST_MASK){}                  //等待时钟切换到外部参考时钟
        while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}
        //进入FBE模式,
        //0x18==25分频=2M,
        //0x11==18分频=2.7778M
        //0x12==19分频=2.63M,
        //0x13==20分频=2.5M
         #if(BUS_CLOCK == 50)
         MCG_C5 = MCG_C5_PRDIV(0x18); //25分频  50
         #elif(BUS_CLOCK == 82)
         MCG_C5 = MCG_C5_PRDIV(0x0e); //15分频  49
         #elif(BUS_CLOCK == 90)
         MCG_C5 = MCG_C5_PRDIV(0x0e); //15分频  54
         #elif(BUS_CLOCK == 100)
         MCG_C5 = MCG_C5_PRDIV(0x0c); //13分频  52
         #elif(BUS_CLOCK == 105)
         MCG_C5 = MCG_C5_PRDIV(0x0c); //13分频  55
         #elif(BUS_CLOCK == 110)
         MCG_C5 = MCG_C5_PRDIV(0x0b); //12分频  53
         #elif(BUS_CLOCK == 115)
         MCG_C5 = MCG_C5_PRDIV(0x0b); //12分频  55
         #endif
        //确保MCG_C6处于复位状态,禁止LOLIE、PLL、和时钟控制器,清PLL VCO分频器
        MCG_C6 = 0x0;
        //保存FMC_PFAPR当前的值
        temp_reg = FMC_PFAPR;
        //通过M&PFD置位M0PFD来禁止预取功能
        FMC_PFAPR |= FMC_PFAPR_M7PFD_MASK | FMC_PFAPR_M6PFD_MASK | FMC_PFAPR_M5PFD_MASK
                         | FMC_PFAPR_M4PFD_MASK | FMC_PFAPR_M3PFD_MASK | FMC_PFAPR_M2PFD_MASK
                         | FMC_PFAPR_M1PFD_MASK | FMC_PFAPR_M0PFD_MASK;
        ///设置系统分频器
        //MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG/8
        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1)
                     | SIM_CLKDIV1_OUTDIV3(0) | SIM_CLKDIV1_OUTDIV4(7);
        //从新存FMC_PFAPR的原始值
        FMC_PFAPR = temp_reg;
        //设置VCO分频器,使能PLL为100MHz, LOLIE=0, PLLS=1, CME=0, VDIV=26
        #if(BUS_CLOCK == 50)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(26);  //VDIV = 26 (x50)
        #elif(BUS_CLOCK == 82)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(25);  //VDIV = 25 (x49)
        #elif(BUS_CLOCK == 90)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(30);  //VDIV = 30 (x54)
        #elif(BUS_CLOCK == 100)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(28);  //VDIV = 28 (x52)
        #elif(BUS_CLOCK == 105)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(31);  //VDIV = 31 (x55)
        #elif(BUS_CLOCK == 110)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(29);  //VDIV = 29 (x53)
        #elif(BUS_CLOCK == 115)
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV(31);  //VDIV = 26 (x55)
        #endif
        while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set
        while (!(MCG_S & MCG_S_LOCK_MASK)){}; // Wait for LOCK bit to set
        //进入PBE模式
        //通过清零CLKS位来进入PEE模式
        // CLKS=0, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0
        MCG_C1 &= ~MCG_C1_CLKS_MASK;
        //等待时钟状态位更新
        while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){};
        //SIM_CLKDIV2 |= SIM_CLKDIV2_USBDIV(1);
        SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTE_MASK
                    | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTA_MASK;  //开启端口时钟
    }
    //------------------------------端口初始化---------------------------------//
    void PORT_Init()
    {
       ////B4~B7设为GPIO输入模式,连接ov7260的8位灰度输入
       PORTC_PCR8 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC8引脚设置为GPIO模式 上拉
       PORTC_PCR9 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC9引脚设置为GPIO模式 上拉
       PORTC_PCR10 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC10引脚设置为GPIO模式 上拉
       PORTC_PCR11 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC11引脚设置为GPIO模式 上拉
       PORTC_PCR12 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC12引脚设置为GPIO模式 上拉
       PORTC_PCR13 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC13引脚设置为GPIO模式 上拉
       PORTC_PCR14 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC14引脚设置为GPIO模式 上拉
       PORTC_PCR15 = PORT_PCR_MUX(1) | PORT_PCR_PE_MASK |  PORT_PCR_PS_MASK;//PTC15引脚设置为GPIO模式 上拉
      
       GPIOC_PDDR = 0xffff00ff;    //C8~C15设置为输入口
    }
    //--------------------------------PWM初始化--------------------------------//
    void PWM_Init()
    {
         PORTB_PCR0 &= ~PORT_PCR_MUX_MASK; //清零
         PORTB_PCR19 = PORT_PCR_MUX(3);  //PTB19 IO口在FTM模式 FTM2_CH1
         PORTC_PCR4 = PORT_PCR_MUX(4);  //PTC4 FTM0_CH3
         PORTA_PCR7 = PORT_PCR_MUX(3); //PTA7 FTM1_CH4
         PORTA_PCR10 = PORT_PCR_MUX(3); //PTA10 FTM2_CH0
         PORTA_PCR12 = PORT_PCR_MUX(3); //PTA12 FTM1_CH0
         PORTA_PCR13 = PORT_PCR_MUX(3); //PTA13 FTM1_CH1
         SIM_SCGC6 |= SIM_SCGC6_FTM1_MASK;//开启FTM1模块时钟
         SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK;//开启FTM0模块时钟
         SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK;//开启FTM2模块时钟
         FTM1_C0SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //配置模式 CH0
         FTM1_C1SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //FTM1_CH1
         FTM2_C1SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //FTM2_CH1
         FTM2_C0SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //FTM2_CH0
         FTM0_C3SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //FTM0_CH3
         FTM0_C4SC |= FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK;  //FTM0_CH4
         FTM0_CNT = 0;
         FTM1_CNT = 0; //设置计数初值为0
         FTM2_CNT = 0;
         FTM1_MOD = 2500; //设置PWM频率为10K
         FTM0_MOD = 10000;
         FTM2_MOD = 40;
         FTM1_CNTIN = 0; //设置初始化计数值
         FTM0_CNTIN = 0;
         FTM2_CNTIN = 0;
         FTM1_C0V = temp_pwm1; //设置占空比
         FTM1_C1V = speed_PWM_R2;
         FTM0_C3V = servo_PWM_R;
         FTM2_C1V = temp_pwm4;
         FTM0_C4V = temp_pwm5;
         FTM2_C0V = temp_pwm6;    //PTA10  产生的PWM作为DMA触发源  2.5M
         FTM1_SC |= FTM_SC_CLKS(1) | FTM_SC_PS(2); //设置时钟和分频
         FTM0_SC |= FTM_SC_CLKS(1) | FTM_SC_PS(7);
         FTM2_SC |= FTM_SC_CLKS(1) | FTM_SC_PS(0);
    }
    void DMA0_Init(void)
    {
    SIM_SCGC6|=SIM_SCGC6_DMAMUX_MASK;//打开DMA多路复用器时钟
    SIM_SCGC7|=SIM_SCGC7_DMA_MASK;//打开DMA模块时钟
    DMAMUX_CHCFG0=DMAMUX_CHCFG_SOURCE(51);//DMA通道0对应51号DMA请求,即PORTC
    //DMA_ERQ|=DMA_ERQ_ERQ0_MASK;//使能通道0硬件DMA请求
    DMA_TCD0_CITER_ELINKNO=DMA_CITER_ELINKNO_CITER(V);//当前主循环次数,采集点数
    DMA_TCD0_BITER_ELINKNO=DMA_BITER_ELINKNO_BITER(V);//起始主循环次数,采集点数
    DMA_TCD0_SADDR=(uint32)&GPIOC_PDIR;//设置源地址GPIO口,PORTC   //PTC8开始的
    DMA_TCD0_SOFF=0;//每次传送源地址不变
    //DMA_TCD1_NBYTES_MLOFFYES=DMA_NBYTES_MLOFFYES_NBYTES(1)+DMA_NBYTES_MLOFFNO_SMLOE_MASK+DMA_NBYTES_MLOFFYES_MLOFF(-4);//传送4字节
    DMA_TCD0_NBYTES_MLNO=DMA_NBYTES_MLNO_NBYTES(1);//每次读取一字节
    DMA_TCD0_SLAST=0;//主循环结束后源地址0回写tcd
    DMA_TCD0_DLASTSGA=0;//主循环结束后目的地址0回写tcd
    DMA_TCD0_DADDR=(uint32)video;//设置目的地址,video数组第一个元素
    DMA_TCD0_DOFF=1;//每次写目的地址加1
    DMA_TCD0_ATTR=DMA_ATTR_SSIZE(0)+DMA_ATTR_DSIZE(0);//源数据宽度8bit,目的数据宽度8bit
    DMA_TCD0_CSR=DMA_CSR_DREQ_MASK;//DMA通道0主循环结束后停止硬件请求//
    //DMA_TCD0_CSR|=DMA_CSR_INTMAJOR_MASK;   出现BBUG   应该改为
        DMA_TCD0_CSR = DMA_CSR_INTMAJOR_MASK;  //使能DMA0中断  //
    DMAMUX_CHCFG0|=DMAMUX_CHCFG_ENBL_MASK;//DMA通道0使能
    dma0++;
    //DMA_TCD1_CSR|=DMA_CSR_INTMAJOR_MASK;//使能DMA中断
    //DMA_TCD0_CSR|=DMA_CSR_startline_MASK;
    }

    //---------------------------行中断捕捉端口初始化-------------------//
    void EXIT_Init()
    {
        PORTC_PCR0 =PORT_PCR_MUX(1) | PORT_PCR_IRQC(1);//C0引脚设置为GPIO模式,上升沿触发DMA请求;
        GPIOC_PDDR &= ~PIN(0);            // 中断必须是输入模式
        PORTA_PCR8=PORT_PCR_MUX(1)|PORT_PCR_IRQC(9);//A8引脚设置为GPIO模式,上升沿中断,行中断

        PORTE_PCR24=PORT_PCR_MUX(1)|PORT_PCR_IRQC(10);//B10引脚设置为GPIO模式,下降沿中断,场中断
    }
    /***************************自定义函数开始*************************/

    void main(void)
    {
        PLL_Init();
       // DMA0_Init();
        PORT_Init();
        PWM_Init();
        EXIT_Init();
        enable_irq(91);                      //打开PTE24 场中断
        EnableInterrupts;
        while(1)                                  //检测起跑线
        {
        }
    }
    //-----------------------------中断函数-----------------------------//
    void DMA_CH0_isr(void)  //中断源由PTA10产生
    {
    DMA_INT |= DMA_INT_INT0_MASK;   //清除通道0中断

    row_F[imagerow]=1;//采集完成标志
    imagerow++;
        disable_irq(0);
    }
    void PTE24_isr(void)//场中断,E24,下降沿中断
    {
      PORTE_PCR24|=PORT_PCR_ISF_MASK;//清除中断标志
      //enable_irq(91);   //按照找老师给的添加的 关场中断
      DMA0_Init();
      enable_irq(0);//使能DMA通道0完成中断
      row=0;//初始化行
      imagerow=0;//初始化采集行
      enable_irq(87);//使能A口中断 ,PTA8行中断
    }

    void PTA8_isr(void)//行中断,A8,上升沿中断
    {
      PORTA_PCR8|=PORT_PCR_ISF_MASK;//清除中断标志位
      row++; //行计数
      if(row==data_table[imagerow])//如果当前行数据应该采集
      {
       DMA_ERQ|=DMA_ERQ_ERQ0_MASK;//使能通道0硬件DMA请求
      }
      else if(row>=endlineROW) //一场完成,关闭行中断
      {
      disable_irq (87);
      }
    }
    上面的程序DMA中断一直进不了,搞了好久不知道原因,希望能看看,灰常谢谢,我将等待你的回复.

    使用特权

    评论回复
    13
    我思故我在12345|  楼主 | 2015-4-23 21:46 | 只看该作者
    fhguo1990 发表于 2015-4-23 21:45
    你好,一直在学习你的K60的学习开发,最近在DMA上遇到了问题!请求指导....以下是数字摄像头程序
    #include  ...

    你的程序太长了有点乱,我的确没精力一行行的去看代码了,建议你对比下我的代码找找原因~

    使用特权

    评论回复
    14
    我是MT| | 2015-4-23 21:46 | 只看该作者
    你好,dma的周期定时触发该怎么用,现在想把u8 temp[100]的数组用pit1周期的触发发送到DAC0,该怎么配置,能把不同的地方告一下吗?。现在pit和da初始化都完成了,就dma没配置出来。
    DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR, DMA_CHn) = (0
    | DMAMUX_CHCFG_ENBL_MASK /* 使能DMA通道 */
    | DMAMUX_CHCFG_TRIG_MASK /* 打开周期性触发模式,注意只有0~3通道支持 */
    | DMAMUX_CHCFG_SOURCE(54) /* 指定DMA触发源 */
    );
    最关键的一句我也进行了如上修改。

    使用特权

    评论回复
    15
    我思故我在12345|  楼主 | 2015-4-23 21:47 | 只看该作者
    我是MT 发表于 2015-4-23 21:46
    你好,dma的周期定时触发该怎么用,现在想把u8 temp[100]的数组用pit1周期的触发发送到DAC0,该怎么配置, ...

    你修改的没问题,你用PIT1的话只能使能通道1,这个你确定吗?还有数组不要用const定义

    使用特权

    评论回复
    16
    我是MT| | 2015-4-23 21:47 | 只看该作者
    我思故我在12345 发表于 2015-4-23 21:47
    你修改的没问题,你用PIT1的话只能使能通道1,这个你确定吗?还有数组不要用const定义 ...

    我用的就是pit1,但还是有些问题。
    对这句话不明白DMA_BASE_PTR->TCD[DMA_CHn].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(Block_Size); /* 1个major loop, 即一次传输量=major_loop*minor_loop,最大为2^15=32767 */
    看数据手册BITER是minor_loop,CITER是major_loop如果按注释的意思,你每次传输量就是 Block_Size*Block_Size了,会很大的啊。
    对这三个寄存器的意思还是不太理解NBYTES,BITER,CITER,lz能说一下它们之间的关系吗?

    使用特权

    评论回复
    17
    我是MT| | 2015-4-23 21:51 | 只看该作者
    我思故我在12345 发表于 2015-4-23 21:47
    你修改的没问题,你用PIT1的话只能使能通道1,这个你确定吗?还有数组不要用const定义 ...

    程序我调通了,PIT暂时还不能周期触发DMA,目前算是个bug吧,勘误手册上有写,后来改为FTM了,害我花了两天时间

    使用特权

    评论回复
    18
    我思故我在12345|  楼主 | 2015-4-23 21:52 | 只看该作者
    我是MT 发表于 2015-4-23 21:51
    程序我调通了,PIT暂时还不能周期触发DMA,目前算是个bug吧,勘误手册上有写,后来改为FTM了,害我花了两 ...

    我晕啊,呵呵,还真没试来,谢谢分享你的经验,另外勘误手册在哪,我也去瞅瞅吧~

    使用特权

    评论回复
    19
    我是MT| | 2015-4-23 21:54 | 只看该作者
    我思故我在12345 发表于 2015-4-23 21:52
    我晕啊,呵呵,还真没试来,谢谢分享你的经验,另外勘误手册在哪,我也去瞅瞅吧~ ...

    官方上搜kinetis_4n30d.pdf第e4588,你看下。

    使用特权

    评论回复
    20
    我思故我在12345|  楼主 | 2015-4-23 21:54 | 只看该作者
    我是MT 发表于 2015-4-23 21:54
    官方上搜kinetis_4n30d.pdf第e4588,你看下。

    好的,谢谢~

    使用特权

    评论回复
    发新帖 我要提问
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    27

    主题

    318

    帖子

    9

    粉丝