发布新日志

  • DEMO_MainMenu

    2014-03-11 13:50:59

    void DEMO_MainMenu(void)
    {
     //DemoRedraw();
     
     GUI_Init();
    //* Draw something on default layer 0
    GUI_SetBkColor(GUI_GREEN);
    GUI_Clear();
    GUI_DispStringHCenterAt("Layer 0", 100, 46);
    //* Draw something on layer 1
    GUI_SelectLayer(1); // Select layer 1
    GUI_SetBkColor(GUI_RED);
    GUI_Clear();
    GUI_SetColor(GUI_BLUE);
    GUI_FillRect(20, 20, 179, 79);
    GUI_SetColor(GUI_WHITE);
    GUI_SetTextMode(GUI_TM_TRANS);
    GUI_DispStringHCenterAt("Layer 1", 100, 46);
    while(1) {
    GUI_Delay(100);
    }
  • FreeRTOS中的软件定时器(Soft Timers)

    2014-03-11 13:30:17

    FreeRTOS内核不提供定时器的功能,但是它却提供了软件定时器的任务进程,这对于有些定时器资源比较紧张的硬件平台而言可有效的帮助解决这一问题。但同时需要注意的是软件定时器的精度是无法和硬件定时器相比较的,因为在软件定时器的定时过程中是极有可能被其它的任务或进程序所打断,由此就引起了时间精度不准的问题(个人实验所测,确实随着时间的推移,定时器精度确实有所下降)。
    关于FreeRTOS软件定时器的原理还未仔细研究源代码,所以具体不是很清晰,但通过官方帮助文档的介绍,软件定时器就是单独作的一个线程在运行,用户代码和软件定时器代码是通过定时器命令队列(Timer Command Queue)来进行通信与交流,当用户根据个人需要对软件定时器作相关的操作后,FreeRTOS内核都会向此队列发送命令,同时软件定时器进程会从这个共用队列中来提取命令,并作出相关的操作。
    其它FreeRTOS的软件定时器功能适用性并不是很高,最简单的应用如官方所说,就是在指定的时间间隔内来调用回调函数(callback Function)关于回调函数一定要注意的是不要尝试在回调函数中引用类似于vTaskDelay()或vTaskDelayUntil()等等阻塞函数,因为这样的话会严重的影响软件定时器的精度与可靠性。同样软件定时器还可配置在单次模式(One-Shot)或自动重载模式(Auto-Reload),具体做法和微控制器硬件定时器做法相差不大。软件定时器也具有运行过程中重设置(Reseting a Timer)功能,相对来说功能还算是比较全面。
    对于软件定时器的使用并不复杂,具体要配置如下宏参数:
    #define configUSE_TIMERS // 使能软件定时器 
    #define configTIMER_TASK_PRIORITY // 确定软件定时器进程优先级(根据具体应用而定,不要设的过低,否则精度也会随之下降)
    #define configQueue_LENGTH // 定时器命令队列(Timer Command Queue)长度
    #define configTIMER_TASK_STACK_DEPTH // 分配给软件定时器任务的内存大小
     
    FreeRTOS为软件定时器提供了如下API函数:
    xTimerCreate();
    xTimerlsTimerActive();
    xTimerStart();
    xTimerStop();
    xTimerChangePeriod();
    xTimerDelete();
    xTimerReset();
    xTimerStartFromISR();
    xTimerStopFromISR();
    xTimerChangePeriodFromISR();
    xTimerResetFromISR();
    xTimerGetTimerID();
    xTimerGetTimerDaemonTaskHandle();
  • uCGUI使用

    2014-03-08 14:27:42

    0:画图函数
       GUI_DrawRect:在当前窗口中的指定位置绘制矩形(不填充,画线颜色为前景色)
           void GUI_DrawRect(int x0, int y0, int x1, int y1);
       GUI_FillRect:在当前窗口中的指定位置绘制填充的矩形区域(填充前景色)
       GUI_ClearRect:清除矩形区域(为矩形区域填充背景颜色,图形界面用填充背景色达到清除屏幕的                               效果
    1:存储设备
        不使用存储设备时,绘制操作直接写入显示器。屏幕在执行绘制操作时随时更新,从而在进行各
    种更新时使屏幕闪烁。如果在此过程中使用存储设备,则所有绘制操作都在存储器中执行。仅在所有操作都完成后才将最终结果显示在屏幕上,其优点是没有闪烁。
        如果不使用存储设备,则可以看到一步步的绘制操作效果,缺点是会出现显示器闪
    烁。使用存储设备时,一次可见到所有例程的效果,就象单次操作一样,不能实际看见中间步骤。
        以下例程是在使用存储设备时通常会调用的,基本用法非常简单:
             1. 创建存储设备(使用GUI_MEMDEV_Create() )。
             2. 激活它(使用GUI_MEMDEV_Select() )。
             3. 执行绘制操作。
             4. 将结果复制到显示器中(使用GUI_MEMDEV_CopyToLCD() )。
             5. 不再需要它时,删除该存储设备(使用 GUI_MEMDEV_Delete() )。

    2:WM窗口管理器
       回调例程:回调例程由用户程序定义,指示在特定事件出现时图形系统调用特定的函数。它们通常用于在窗口内容更改时自动重绘窗口。窗口管理器的默认特性是向每个需要重绘的窗口发送一条 WM_PAINT 。
       当用户对窗口有操作时,WM会发送相应的消息给该窗口,窗口可通过回调函数根据消息直接对屏(没有用存储设备时)或对窗口的存储设备进行操作再拷贝到屏幕上,具体的消息说明可以参考emWin的中文手册。
       WM_SetCallback 
          设置窗口的回调例程:WM_CALLBACK* WM_SetCallback (WM_HWIN hWin, WM_CALLBACK* cb) 
          有了这个函数就可以动态的选择操作的重绘操作等消息的响应动作了。
      WM_SetDesktopColor
          设置桌面窗口的颜色:WGUI_COLOR WM_SetDesktopColor(GUI_COLOR Color); 
             桌面窗口由视窗管理器自动创建,总是覆盖整个显示区域。它始终是一个最底层的窗口,没有定义其          他窗口,它就是默认活动窗口。所有窗口都是桌面窗口的继承窗口。桌面窗口的默认设置不用于自身          重新着色。如果不调用此函数,则桌面窗口不进行重绘;因此其它窗口将保持可见,即使在将它们删          除之后。一旦使用此函数指定了颜色,则桌面窗口将进行自身重新着色。为了恢复默认设置,请调用          此函数并指定GUI_INVALID_COLOR 。
             对桌面窗口颜色设置后,需要调用WM_Exec(),通过执行回调函数重绘无效窗口(针对所有作业),这          样新的桌面窗口的颜色就会显现。
       GUI_Exec【重要、重要,进行操作动作后,一般都要会通过一个GUI延时函数(GUI_Delay)来间接调用该函数,也可以单独建立一个任务来执行GUI_Exec()函数
       GUI_Exec()可以理解为GUI执行函数,调用了很多操作函数或发生了相关的动作,最后都需要通过GUI_Exec()函数通过各个回调函数来一一执行,所以函数名叫GUI_Exec
       此函数将自动重复调用WM_Exec1(),直至完成所有作业 –  实质是直至返回0 值为止。建议调用函数GUI_Exec()作为代替。通常,此函数无需由用户应用程序调用它自动由GUI_Delay() 调用,所以一般作业后都调用一个GUI_Delay(),使作业显现出来。如果使用的是多任务系统,建议通过单独的任务执行此函数,如下所示:
          void ExecIdleTask(void) 
          {
              while(1) {
                 WM_Exec();//调用WM_Exec()完成到显示屏的显示作业(我的理解是:(类似于)调用该函数前,其他的函数执行绘制操作WM_Exec完成将结果复制到显示器中
              }
          }
        WM_InvalidateWindow:调用此函数会告诉WM 指定的窗口未更新
            WM_PAINT消息: 窗口变为无效并应重绘时,发送到窗口。
            void WM_InvalidateWindow(WM_HWIN hWin);
        
        WM_BringToTop:将窗口放在其同属窗口前面
    void WM_BringToTop(WM_HWIN hWin);该窗口将放置在其他所有同属窗口及父窗口的顶部,加入同属于A窗口的B、C两个子窗口,B先C后,则可以通过该函数将B又放在前面。
        WM_BringToBottom:窗口将放置在所有其他同属窗口的下面,但将留在其父窗口的前面。
          void WM_BringToBottom(WM_HWIN hWin); 
          例:
          WM_BringToBottom(_hWindow1);
          GUIDEMO_Delay(SPEED);
        WM_HideWindow:使指定窗口不可见。
          void WM_HideWindow(WM_HWIN hWin);调用此函数后,窗口不会立即“不可见”。在执行WM_Exec() 时,其它窗口的无效区域(出现在要隐藏窗口“后面”的区域)将重绘。隐藏父窗口时,父窗口上的子窗口也会消息。
         WM_HideWindow(hChild2);
         GUIDEMO_Delay(1000/3);
        WM_ShowWindow:使指定窗口显示。
         WM_ShowWindow(hWindow1);
         GUIDEMO_Delay(1000/3);
        WM_GetInsideRect:
          void WM_GetInsideRect(GUI_RECT* pRect);
          返回客户区的坐标,该区域由活动小工具尺寸减去边界尺寸确定。此函数向活动窗口发送一条消息,检
    索内部矩形。如果小工具不处理此消息(也即意味着小工具没有边界),则需使用 WM_GetClientRect函数计算出矩形。结果通过窗口坐标给出。也即, GUI_RECT结构中的 x0和y0相当于x 和y 的边界尺寸, x1和y1相当于窗口尺寸减去边界尺寸 -1。
           客户区:一个窗口的客户区简单地说是它的可使用区。如果一个窗口包括一个边框或标题栏,则客户区是内部的矩形区域。如果没有这样一个 边框或标题栏 ,则客户区等于窗口本身
       WM_GetWindowSizeX/WM_GetWindowSizeY:
          int WM_GetWindowSizeX(WM_HWIN hWin)    ...Y(WM_HWIN hWin) 
          返回窗口的水平尺寸(宽度)              ,返回窗口的垂直尺寸(高度)
          WM_GetWindowSizeX(pMsg->hWin);pMsg为发送给窗口的消息。
      WM_DefaultProc:窗口消息的默认处理函数
          void WM_DefaultProc(WM_MESSAGE* pMsg);
      WM_MoveTo():将指定窗口移动到某个位置
          void WM_MoveTo(WM_HWIN hWin, int x, int y);父窗口移动时,子窗口是跟着做相应移动;
    WM_MoveTo使用说明
       WM_MoveTo刚开始用的时候,移动一个窗口,窗口一直会留下运动的痕迹;
       后来学习别人的例程,了解到,所有窗口都是桌面窗口的继承窗口;因为窗口移动,不仅被移动的窗口要重绘,桌面窗口也要重绘,所以必须给桌面通过 WM_SetCallback(WM_GetDesktopWindow(), BkWindow)函数配置一个桌面的回调函数,在回调函数中处理重绘信息WM_PAINT时,要设置好背景色(与桌面窗口的背景一致),然后调用GUI_Clear(),这样移动窗口的移动痕迹就没有了,同理子窗口在父窗口中移动时,父窗口的WM_PAINT也要如此处理,即一个原则就是要在重绘时清除移动窗口所在的上层窗口,以去掉痕迹。
      WM_MoveWindow():将指定窗口移动某段距离
          void WM_MoveWindow(WM_HWIN hWin, int dx, int dy);
      WM_ResizeWindow:通过增加(或减少)给定差别更改指定窗口的尺寸
          WM_ResizeWindow(_hWindow2, -1, -1);
          GUI_Delay(tDiff);
      WM_DeleteWindow:删除一个窗口
    ///////////////窗口设计shil///////////////////
    下面就是一个窗口的一个设计,建立窗口后,在窗口的区域默认是黑色;且窗口使用存储设备;建好之后,是不会直接显示出来的,但可通过各种途径调用WM_EXEC()来完成作业(这里是GUIDEMO_Delay);
    建立好之后,在它的回调函数里面设置各个消息的处理操作;
    用户对操作的各种操作,都会向串口发送对应的消息;消息都可以通过WM_DefaultProc进行默认处理;如果要想达到一定效果可以分开处理,这里要重绘窗口,处理WM_PAINT消息,在这个消息处理中先设置好环境颜色,在窗口区域内画了一个矩形显示了汉子,做出一个彩色窗口。
    【改变窗口背景两种方法:1.在窗口的回调函数的WM_PAINT中先设置背景色,再GUI_Clear()
                            2.在窗口的回调函数的WM_PAINT中先设置背景色,再可以画框等
    【一个窗口会接收到不同的消息,用回调函数来处理消息是窗口最合适的选择,尤其是窗体重绘WM_PAINT】
    static void Window1(WM_MESSAGE* pMsg) {
      GUI_RECT Rect;
      int x, y;
      switch (pMsg->MsgId) {
      case WM_PAINT:
        WM_GetInsideRect(&Rect);
        GUI_SetBkColor(GUI_RED);
        GUI_SetColor(GUI_YELLOW);
        GUI_ClearRectEx(&Rect);
        GUI_DrawRectEx(&Rect);
        GUI_SetColor(GUI_WHITE);
        GUI_SetFont(&GUI_Font24_ASCII);
        x = WM_GetWindowSizeX(pMsg->hWin);
        y = WM_GetWindowSizeY(pMsg->hWin);
        GUI_DispStringHCenterAt("Window 1", x / 2, (y / 2) - 12);
        break;
      default:
        WM_DefaultProc(pMsg); //要处理WM_GetInsideRect发过来的消息(检索内部矩形的大小)
      }
    }
    static void cbChild(WM_MESSAGE* pMsg) {
      int x, y;
      switch (pMsg->MsgId) {
      case WM_PAINT:
        GUI_SetBkColor(GUI_WHITE);
        GUI_Clear(); //清除的范围是子窗口即该回调函数对应的窗口,其他的窗口均不受影响
        GUI_SetColor(GUI_RED);
        GUI_SetFont(&GUI_Font24_ASCII);
        x = WM_GetWindowSizeX(pMsg->hWin);
        y = WM_GetWindowSizeY(pMsg->hWin);
        GUI_DispStringHCenterAt("Child window", x / 2, (y / 2) - 12);
        break;
      default:
        WM_DefaultProc(pMsg);
      }
    }


    WM_HWIN hWindow1;
    hWindow1 = WM_CreateWindow( 50,  70, 165, 100, WM_CF_SHOW|WM_CF_MEMDEV , Window1, 0);
    GUIDEMO_Delay(1000/3);
    hChild = WM_CreateWindowAsChild(10, 50, 145, 40, hWindow1, WM_CF_SHOW | WM_CF_MEMDEV, cbChild, 0);
    GUIDEMO_Delay(1000);



    /////////////////////////////////////////////
    3:GUI环境设置
      背景、前景颜色设置(这里只是设置颜色,需要通过GUI_Clear()全部换成新的背景色)
          设置(还有有读取)当前背景颜色:GUI_COLOR GUI_SetBkColor(GUI_COLOR Color)
          设置(还有有读取)当前前景色:   void GUI_SetColor(GUI_COLOR Color);
     GUI_Clear()清除背景时,清除的范围是调用函数时所多对应的窗口,其他的窗口(包括桌面窗口)等均不受影响, 
      设置用于文本输出的字体
          GUI_SetFont() 设置当前字体:const GUI_FONT * GUI_SetFont(const GUI_FONT * pNewFont);
                         GUI_SetFont(&GUI_Font24_ASCII);
          GUI_SetDefaultFont() 设置默认字体

    4:通过触摸屏获取输入信息
      int CursorX 、CursorY ;
      CursorX = GUI_TOUCH_GetxPhys();
      CursorY = GUI_TOUCH_GetyPhys();
      GUI_CURSOR_SetPosition(CursorX,CursorY);
      GUI_Delay(100); //调用GUI_EXEC() 完成操作作业

      在UC/OS中,一般通过建立一个高优先级的任务,来通过GUI_TOUCH_Exec()扫描触摸屏,获得
    static void taskA (void *p_arg) 
    {
    u8 tick=0;
    (void)p_arg;
    while(DEF_TRUE) 
     {
     OSTimeDlyHMSM(0,0,0,10); 
     GUI_TOUCH_Exec(); 
      }
    }
    GUI_TOUCH_Exec()在这个函数中调用TOUCH_X_MeasureY  TOUCH_X_MeasureX检测触摸屏的触摸位置,保存在xPhys、yPhys 中,应用层可以通过GUI_TOUCH_GetyPhys来获取这个值。
    yPhys = TOUCH_X_MeasureY();
    xPhys = TOUCH_X_MeasureX();

    int  GUI_TOUCH_GetyPhys(void) {
      return yPhys;
    }
    5:窗口对象(小工具)
     1:BUTTON:可按下的按钮。按钮上可显示文本或位图
            int Key;
            BUTTON_Handle hButton1;
            hButton1 = BUTTON_CreateEx(10,10,50,50,0,WM_CF_SHOW,0,'1'); 
            BUTTON_SetText(hButton1,"hello!");//按键上显示的文字
            do {
                Key = GUI_GetKey();//BUTTON创建后,会响应触摸屏的触摸消息;通过GUI_GetKey()可以获取键缓冲器中的字符编码;若未缓冲键,则返回值为0,缓冲键可以理解为被按下的 BUTTON,若有BUTTON被按下,它的ID值就会传送到键缓冲器中,用GUI_GetKey可以读到。  
                代码://此处需根据Key值或超时退出while循环等待
                GUI_Delay(100);//内部调用GUI_Exec(); 有事件或操作任务需要通过GUI_Exec()来最终执行,
             } while()
     
     2: EDIT:文本编辑框控件,编辑区通常用作输入文本的主要用户接口
            EDIT_Handle   hEdit;
            hEdit = EDIT_Create( 200, 114, 100, 25, ' ', 80, 0 );
            EDIT_SetFont(hEdit, &GUI_Font8x16);
            GUIDEMO_Delay(100);
    do {
      Key = GUIDEMO_WaitKey();//内部包含GUI_GetKey()与GUI_Exec(),获取缓冲键的ID
      switch (Key) {
      case 0:
      case GUI_ID_CANCEL:
       break;
      default:
      EDIT_AddKey(hEdit, Key);//在EDIT中显示Key
       }
    } while ((Key != '3') && (Key!=GUI_ID_CANCEL));
    /////EDIT_GetText() 获取用户输入
  • 【STM32F429-DISCOVERY学习笔记】STM32F429驱动SDRAM(IS42S16400J)详解

    2014-03-07 20:38:02

      驱动SDRAM的时序比较的麻烦一些,不像驱动SRAM,非常简单,网上搜索一下,估计有非常多的FPGA驱
    动SDRAM的资料,而且是各种的给你讲时序问题,现在F429/439集成了控制器以后就方便很多了,用户只需配
    相应的寄存器即可,这里向大家推荐一篇文章,强烈的推荐,不懂SDRAM为何物的,一定要看看。
    《高手进阶,终极内存技术指南——完整/进阶版》 http://bbs.armfly.com/read.php?tid=1930
    1.  学习SDRAM驱动前的准备工作
        学习SDRAM前搞清楚两个问题,一个是SDRAM的基本原理,还有一个就是那几个关键的参数,参数是F429
    配置SDRAM的关键,这几个参数大概知道是什么意思就行了,配置的时候,根据SDRAM的手册配置一下就OK
    了。在STM32F429/439的数据手册里关键参数说明,F429/439是把这几个关键的参数做到了一个存器里面
    了,这些参数,手册上面有一些英文说明,但比较的笼统。

    我推荐的那篇文章,建议大家一定要看,别的可以不看,这个必须得看,讲的实在太好了,我这里把一些关键的
    参数摘录出来:
    tRCD:
           在发送列读写命令时必须要与行有效命令有一个间隔,这个间隔被定义为tRCD,即RAS to CAS
    Delay(RAS至CAS延迟),大家也可以理解为行选通周期,这应该是根据芯片存储阵列电子元件响
    应时间(从一种状态到另一种状态变化的过程)所制定的延迟。tRCDSDRAM的一个重要时序参数
    ,可以通过主板BIOS经过北桥芯片进行调整,但不能超过厂商的预定范围。广义的tRCD以时钟周期
    tCKClock Time)数为单位,比如tRCD=2,就代表延迟周期为两个时钟周期,具体到确切的时
    ,则要根据时钟频率而定,对于PC100SDRAMtRCD=2,代表20ns的延迟,对于PC133则为15ns
    CL(CAS Latency):
            在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据I/O通道(DQ)输
    出到内存总线上了。但是在CAS发出之后,仍要经过一定的时间才能有数据输出,从CAS与读取命令发
    出到第一笔数据输出的这段时间,被定义为CL(CAS Latency,CAS潜伏期)。由于CL只在读取时出现,
    以CL又被称为读取潜伏期(RL,Read Latency)。CL的单位与tRCD一样,为时钟周期数,具体耗时
    时钟频率决定。
           数据写入的操作也是在tRCD之后进行,但此时没有了CL(记住,CL只出现在读取操作中)。
    tWR:
            数据并不是即时地写入存储电容,因为选通三极管(就如读取时一样)与电容的充电必须要有一段
    时间,所以数据的真正写入需要一定的周期。为了保证数据的可靠写入,都会留出足够的写入/校正时间
    tWRWriteRecovery Time),这个操作也被称作写回(Write Back)。
    tRP:
            在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开新的工作行,这个间隔被
    称为tRP(Precharge command Period,预充电有效周期)。和tRCDCL一样,tRP的单位也是时钟周
    期数,具体值视时钟频率而定。
    我这里就先贴上这几个参数,其它的参数,大家可以查阅相关的资料。
    2.  F429/439手册中对SDRAM的介绍
           如果你看了我在前面推荐的那个文章,现在看这个手册还是比较容易的。我这里就象征性的贴一下F429
    439自带SDRAM控制器的特性和引脚,其它的大家自己看手册就行。



    3.  F429/439驱动SDRAM详细过程
    说明:以官方的Demonstration做例子


    第一步:F429的时钟配置
            F429/439支持最高主频180Mhz,相比于407只提高了12M, 感觉劲还是不足,传说STM32F5系列将
    做到250MHz,就相当的给力了。
    F429配置过程和以前芯片的配置过程是一样的,我这里主要是想通过这个配置得到SDRAM的时钟频率, 先
    说明一下这个开发板外接的是8MHz的晶振,通过锁相环得到180的主频。

    #define PLL_SOURCE_HSE    // HSE (8MHz) used to clock the PLL, and the PLL is used as system clock source
    /*  PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N  */
    #if defined  (PLL_SOURCE_HSI)
    #define PLL_M      16
    #else
    #define PLL_M      8
    #endif
    #define PLL_N      360
    /* SYSCLK = PLL_VCO / PLL_P */  
    #define PLL_P      2
    /* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
    #define PLL_Q      7
    由上面的公式得到 :SYSCLK = PLL_VCO / PLL_P =( (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N)/PLL_P  = 180
    /* HCLK = SYSCLK / 1 = 180 */RCC->CFGR |= RCC_CFGR_HPRE_DIV1;  
    /* PCLK2 = HCLK / 2 = 90 */RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;  
    /* PCLK1 = HCLK / 4 = 45 */RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
    第二步:引脚配置
            驱动前要先搞清楚一个问题,驱动SDRAM的行选和列选的地址线是分时复用的,和SRAM不同
    需要完整的地址线才可以访问各个地址空间,官方提供的原理图


    关于这个芯片的特点:


    标准的SDRAM一般都是4个BANK,这个芯片也不例外,芯片的总容量
    1Mbitx 16-bit x 4-bank = 67,108,864 bits = 64-Mbit ,每个BANK的组成
    4096rows x 256 columns x 16 bits, 这个比较重要,配置的时候要用到,也就是
    12行8列。
    第三步:FMC配置
    这里FMC驱动SDRAM只支持两种频率,分别是
    SDCLK period = 2 x HCLK periods
    SDCLK period = 3 x HCLK periods
    根据我们上面的配置,HCLK是180MHz,这里SDCLK就是2分频或者3分频,官方提供的
    这个例子,在注释上面有误,他们是按照主频168MHz,2分频是SDCLK = 84MHz注释的,
    不过不影响使用,只是注释上面是这样的。按照90MHz的话,每个时钟周期就是11.1ns
    7个SDRAM关键参数的配置,下面一个一个的说。
    1.  TMRD
    /* TMRD: 2 Clock cycles */
    FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2;
    这里为什么成2,查手册, 手册上提供的是三种速度等级时提供的参数,我们这里是用
    的90MHz,也取2个肯定是没问题的。


    2. TXSR
    /* TXSR: min=70ns (6x11.10ns) */
    FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7;
    开发板上面用的SDRAM速度等级的7,最高工作频率时143MHz


    3. TRAS
    /* TRAS: min=42ns (4x11.10ns) max=120k (ns) */
    FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;


    4. TRC
    /* TRC:  min=63 (6x11.10ns) */        
    FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 7;    


    5. TWR
    /* TWR:  2 Clock cycles */
    FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;  

    6. TRP
    /* TRP:  15ns => 2x11.10ns */
    FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2;


    7. TRCD              
    /* TRCD: 15ns => 2x11.10ns */
    FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;


    时序设置好以后就是SDRAM控制器的配置。

    /* 支持两个SDRAM的BANK,这里使用的是bank2  */
      FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank2_SDRAM;
      /* 根据这个SDRAM的特性是12行,8列,在这里配置一下 */
      FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b;
      FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b;
      /* 数据位宽是16 */
      FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;
      /* 此芯片支持4个bank */
      FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
      /* 设置CAS的延迟是3 */
      FMC_SDRAMInitStructure.FMC_CASLatency = SDRAM_CAS_LATENCY;
      FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
      /* 配置SDCLK的时钟频率 */
      FMC_SDRAMInitStructure.FMC_SDClockPeriod = SDCLOCK_PERIOD;  
      /* 禁止读取时的突发模式 */
      FMC_SDRAMInitStructure.FMC_ReadBurst = SDRAM_READBURST;
      /* 设置ReadPipe时的延迟  */
      FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1;
      FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure;
    第四步:SDRAM初始化

    1.  使能时钟
      FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled;  
      FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;
      FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;  FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;
      /* Wait until the SDRAM controller is ready */  

      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
      {  }
      /* Send the command */  

      FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
    2.  插入100ms的延迟

      __Delay(10);
    3.  预充电配置  
      /* Configure a PALL (precharge all) command */  
      FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL;
      FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
      FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
      FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;  
      /* Wait until the SDRAM controller is ready */
      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
      {
      }  
      /* Send the command */
      FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
    4. 自动刷新配置
      /* Configure a Auto-Refresh command */  
      FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh;
      FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
      FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 4;
      FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;  
      /* Wait until the SDRAM controller is ready */
      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
      {
      }  
      /* Send the  first command */
      FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);  
      /* Wait until the SDRAM controller is ready */  
      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
      {  }
      /* Send the second command */  
      FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
    5. 配置外部模式寄存器  
      /* Program the external memory mode register */
      tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2          |                  
                       SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
                       SDRAM_MODEREG_CAS_LATENCY_3           |                  
                       SDRAM_MODEREG_OPERATING_MODE_STANDARD |
                       SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;  
      /* Configure a load Mode register command*/  
      FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode;
      FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
      FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
      FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr;  
      /* Wait until the SDRAM controller is ready */
      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
      {
      }  
      /* Send the command */
      FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
    6. 刷新频率设置
      /* Set the refresh rate counter */  
      /* (7.81 us x Freq) - 20 */
      /* Set the device refresh counter */  
      FMC_SetRefreshCount(683);
      /* Wait until the SDRAM controller is ready */  
      while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
      {  }
    关于刷新频率的的数值是这么得到的,这里得详细的说一下,目前公认的标准是,存储体中电容的数据有效保存期上限是
    64ms(毫秒,1/1000秒),也就是说每一行刷新的循环周期是64ms。这样刷新速度就是:行数量/64ms。我们在看内
    存规格时,经常会看到4096 Refresh Cycles/64ms或8192 RefreshCycles/64ms的标识,这里的4096与8192就代表这
    个芯片中每个L-Bank的行数。刷新命令一次对一行有效,发送间隔也是随总行数而变化,4096行时为15.625μs(微秒,
    1/1000毫秒),8192行时就为7.8125μs。        
           SDRAM的手册上说是4096 refresh cycles every 64 ms,这里就应该是15.525us,而注释上面是按8192行计算
    ,郁闷,有时间得研究研究。


    到这里基本就设置完了,剩下就可以像使用SRAM一样,使用SDRAM了。


  • STM32F1和STM32F4 区别

    2014-03-07 20:21:50

    STM32F4相对于STM32F1的改进不只一点点,为了便于初学者了解,我们比对相关资料将改进点进行了汇总。

    STM32F1和STM32F4 区别   (安富莱整理)

    u  F1采用Crotex M3内核,F4采用Crotex M4内核。
    u  F1最高主频 72MHz, F4最高主频168MHz。
    u  F4具有单精度浮点运算单元,F1没有浮点运算单元。
    u  F4的具备增强的DSP指令集。F4的执行16位DSP指令的时间只有F1的30%~70%。F4执行32位DSP指令 的时间只有F1的25%~60%。
    u  F1内部SRAM最大64K字节, F4内部SRAM有192K字节(112K+64K+16K)。
    u  F4有备份域SRAM(通过Vbat供电保持数据),F1没有备份域SRAM。
    u  F4从内部SRAM和外部FSMC存储器执行程序的速度比F1快很多。F1的指令总线I-Bus只接到Flash上,从SRAM和FSMC取指令只能通过S-Bus,速度较慢。F4的I-Bus不但连接到Flash上,而且还连接到SRAM和FSMC上,从而加快从SRAM或FSMC取指令的速度。
    u  F1最大封装为144脚,可提供112个GPIO;F4最大封装有176脚,可提供140个GPIO。
    u  F1的GPIO的内部上下拉电阻配置仅仅针对输入模式有用,输出时无效。而F4的GPIO在设置为输出模式时,上下拉电阻的配置依然有效。即F4可以配置为开漏输出,内部上拉电阻使能,而F1不行。
    u  F4的GPIO最高翻转速度为84MHz,F1最大翻转速度只有18MHz。
    u  F1最多可提供5个UART串口,F4最多可以提供6个UART串口。
    u  F1可提供2个I2C接口,F4可以提供3个I2C接口。
    u  F1和F4都具有3个12位的独立ADC,F1可提供21个输入通道,F4可以提供24个输入通道。F1的ADC最大采样频率为1Msps,2路交替采样可到2Msps(F1不支持3路交替采样)。F4的ADC最大采样频率为2.4Msps,3路交替采样可到7.2Msps。
    u  F1只有12个DMA通道,F4有16个DMA通道。F4的每个DMA通道有4*32位FIFO,F1没有FIFO。
    u  F1的SPI时钟最高速度为 18MHz, F4可以到37.5MHz。
    u  F1没有独立的32位定时器(32位需要级联实现),F4的TIM2和TIM5具有32位上下计数功能。
    u  F1和F4都有2个I2S接口,但是F1的I2S只支持半双工(同一时刻要么放音,要么录音),而F4的I2S支持全双工,放音和录音可以同时进行。
  • emWin 介绍

    2014-03-07 17:18:32

    都是Segger 公司的产品。
    Segger公司的图形系统正式名字叫 emWin。
    ucGui 是Segger公司为 Micrium 公司(推出uCOS-II 和 uCOS-III的公司)量身定制的 emWin,换个名字而已。不过目网上流传的ucGui版本相对比较陈旧,还是 3.X 的版本。
    目前 emWin 版本已经到 5.X 了,底层驱动接口变化有点大。

    最近,Segger 公司为 ST公司量身定制的 emWin 叫 STemWin, 更加适合STM32使用。

    (ST)进一步扩大其微控制器性能领先优势,让智能化技术无处不在,最新的STM32 ARM Cortex微控制器拥有市场领先的性能和图形处理功能,意法半导体与SEGGER达成的软件协议让用户界面可提供更丰富的功能.

    横跨多重电子应用领域、半导体供应商意法半导体宣布新系列微控制器首批样片现已上市。新系列微控制器整合目前性能最高的180MHz ARM Cortex-M4内核和图形增强技术,实现了功能更丰富的用户体验,意法半导体还携手市场领先的软件厂商SEGGER为嵌入式图形开发提供更多的软件支持。此外,意法半导体宣布集成超大存储容量的168MHz系列已投入量产,该产品在2012年起为主要客户提供样片。


    全新180MHz STM32F429/39系列进一步扩大STM32系列在Cortex-M微控制器市场的性能领先优势,新的制造工艺和设计技术降低了停止模式的电流消耗,可延长便携应用的电池寿命。此外,STM32F429/39拥有多项新功能,如TFT-LCD控制器、加快图形处理性能的ST Chrom-ART Accelerator和SDRAM存储器接口,让智能电表、小家电、工业设备及医疗设备等应用的图形用户界面拥有更丰富、更绚丽的内容以及更直观的操作特性。


    I2S TDM(集成电路互连音频时分多路复用)接口可实现多路音频设计。新系列的部分产品还为嵌入式处理器提供最新及最强大的防复制安全功能,全新STemWin图形软件还可访问SEGGER emWin嵌入式图形软件栈,充分发挥这些新的图形硬件功能的优势。


    意法半导体向STM32客户免费提供STemWin软件。该软件内置SEGGER VNC虚拟网络计算系统,可运用互联网协议远程查看用户界面。该软件还为开发人员提供其它强大的功能,其中包括窗口管理器和小工具包、触摸屏/鼠标支持,存储器上下文可实现无闪屏刷新。

    意法半导体微控制器部总经理Michel Buffa表示:“随着STM32F429/39系列上市,STM32系列又树立一个新的重要标准。我们非常高兴与SEGGER携手及时为客户开发出高品质图形开发工具STemWin,让我们的客户能够充分发挥STM32F429/39系列的新功能。”


  • NVIC_PriorityGroupConfig()说明(

    2014-03-05 10:48:21

    STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

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

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

    看了上面的介绍后,相信大家都明白了这里面的关系了,总结下便是:抢占式优先级>响应优先级>中断表中的排位顺序(其中“>”理解为比较的方向)。

        正是因为每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

    1.     所有8位用于指定响应优先级

    2.     最高1位用于指定抢占式优先级,最低7位用于指定响应优先级

    3.     最高2位用于指定抢占式优先级,最低6位用于指定响应优先级

    4.     最高3位用于指定抢占式优先级,最低5位用于指定响应优先级

    5.     最高4位用于指定抢占式优先级,最低4位用于指定响应优先级

    6.     最高5位用于指定抢占式优先级,最低3位用于指定响应优先级

    7.     最高6位用于指定抢占式优先级,最低2位用于指定响应优先级

    8.     最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

        以上便是优先级分组的概念,但是Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

       第0组:所有4位用于指定响应优先级

       第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级

       第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级

       第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级

       第4组:所有4位用于指定抢占式优先级

    这里便对于于文章最前提到的固件库里相关的函数了——NVIC_PriorityGroupConfig(u32   NVIC_PriorityGroup),函数的参数共有5种:

        这个函数的参数(NVIC_PriorityGroup值)有下列5种:

        NVIC_PriorityGroup_0 => 选择第0组

        NVIC_PriorityGroup_1 => 选择第1组

        NVIC_PriorityGroup_2 => 选择第2组

        NVIC_PriorityGroup_3 => 选择第3组

        NVIC_PriorityGroup_4 => 选择第4组

        这其实也很好理解,比如选择NVIC_PriorityGroup_1,那么抢占式优先级便占一位,也就是说可以有2^1个级别,可以设置为0和1,而响应优先级则占3位,也就是说可以有2^3个选择,可以设置为0~7;总共来说就可以区别>16种优先级(为什么大于而不是等于,想想就应该明白了)

       举个例子吧,假如现在有4个外部中断,还有一个EXTI9_5中断,那么如果选择优先级分组为第1组,那么抢占式优先级便只有两种,5个中断就至少有3个在抢占式优先级上是相同的优先级上,其他两个在令一优先级别。接着设置响应优先级可以有8种选择;假如现在同时有两个抢占式优先级别相同的中断发生,那么处理的顺序是谁的响应优先级高则谁优先进入中断,另外这点是需要注意的,如果此时进入这个中断之后又来了一个抢占式优先级相同但是响应优先级更高的中断,这时也是不会打断已有的中断的
  • STM32中DMA的使用入门

    2014-03-04 11:43:32

    今天刚看了一个ADC采集的程序,和之前用其他单片机写的程序还是有比较大的差别,这个差别主要就在DMA。这里面总结一下,有一部分是转载别人的。

    什么是STM32DMA?其全称是:Direct Memory Access;根据ST公司提供的相关信息,DMASTM32中一个独立与Cortex-M3内核的模块,有点类似与ADCPWMTIMER等模块;主要功能是通信“桥梁”的作用,可以将所有外设映射的寄存器“连接”起来,这样就可以高速问各寄存器,其传输不受CPU的支配,传输还是双向的;例如,从“表面”上看,它可以将flash中的数据与储存器中变量建立通讯,还可以将一外设的积存器或缓冲器与另外设的寄存器或缓冲器建立双向通讯,有点像把外设硬件之间用“导线”连接在一起了。其间的通讯不占CPU资源,访问速度高,对于实时性强的应用将是一个很好的选择;当然,对于实时性非常强的,建议还是采用专用的DSP芯片。

    怎样启用DMA?首先,众所周知的是初始化,任何设备启用前都要对其进行初始化,要对模块初始化,还要先了解该模块相应的结构及其函数,以便正确的设置;由于DMA较为复杂,我就只谈谈DMA的基本结构和和常用函数,这些都是ST公司提供在库函数中的。

    1、  下面代码是一个标准DMA设置,当然实际应用中可根据实际情况进行裁减:

     

      DMA_DeInit(DMA_Channel1);

      上面这句是给DMA配置通道,根据ST提供的资料,STM3210FxDMA包含7个通道(CH1~CH7),这里需要选择一个传输通道

     

      DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;

      上面语句中的DMA_InitStructure是一个DMA结构体,在库中有声明了,当然使用时就要先定义了;DMA_PeripheralBaseAddr是该结构体中一个数据成员,给DMA一个起始地址,好比是一个buffer起始地址,数据流程是:外设寄存器à DMA_PeripheralBaseAddàmemory中变量空间(或flash中数据空间等),ADC1_DR_Address是ADC1的地址,既然是桥梁,肯定要连接两个端点,这里需要明白所需要连接的外设的地址;

     

      DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;

      上面这句很显然是DMA要连接在Memory中变量的地址,ADC_ConvertedValue是我自己在memory中定义的一个变量;

     

      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

      上面的这句是设置DMA的传输方向,就如前面我所说的,DMA可以双向传输,也可以单向传输,这里设置的是单向传输,如果需要双向传输:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。

     

     

     DMA_InitStructure.DMA_BufferSize = 2;

      上面的这句是设置DMA在传输时缓冲区的长度,前面有定义过了buffer的起始地址:ADC1_DR_Address ,为了安全性和可靠性,一般需要给buffer定义一个储存片区,这个参数的单位有三种类型:ByteHalfWordword,我设置的2half-word(见下面的设置)32位的MCU1half-word16 bits

     

     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      上面的这句是设置DMA的外设递增模式,如果DMA选用的通道(CHx)有多个外设连接,需要使用外设递增模式:DMA_PeripheralInc_Enable;我的例子里DMA只与ADC1建立了联系,所以选用DMA_PeripheralInc_Disable

     

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    上面的这句是设置DMA的内存递增模式,DMA访问多个内存参数时,需要使用DMA_MemoryInc_Enable,当DMA只访问一个内存参数时,可设置成:DMA_MemoryInc_Disable

     

     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

    上面的这句是设置DMA在访问时每次操作的数据长度。有三种数据长度类型,前面已经讲过了,这里不在叙述。

     

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

    与上面雷同。在此不再说明。

     

      DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

      上面的这句是设置DMA的传输模式:连续不断的循环模式,若只想访问一次后就不要访问了(或按指令操作来反问,也就是想要它访问的时候就访问,不要它访问的时候就停止),可以设置成通用模式:DMA_Mode_Normal

     

      DMA_InitStructure.DMA_Priority = DMA_Priority_High;

      上面的这句是设置DMA的优先级别:可以分为4级:VeryHighHigh,Medium,Low.

     

      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

      上面的这句是设置DMA2memory中的变量互相访问的

     

      DMA_Init(DMA_Channel1,&DMA_InitStructure);

      前面那些都是对DMA结构体成员的设置,在次再统一对DMA整个模块做一次初始化,使得DMA各成员与上面的参数一致。

     

      /*DMA Enable*/

      DMA_Cmd(DMA_Channel1,ENABLE);

    使能此通道。

    当然更多的还需要看固件库函数的说明。

     

     

    这里面知识DMA的一个简单应用,后面做很多设计在数据传输上需要做这种转变,特别是像我这样从单片机学过来的,现在里面有了更好的方式要学会去运用,不能固守原来单片机的设计思路。这个相信也是后面学习中很重要的一部分。

Open Toolbar