EDN首页   博客首页

47

关于投票
STM32启动代码概述

一般嵌入式开发流程就是先建立一个工程,再编写源文件,然后进行编译,把所有的 *.s文件和 *.c文件编译成一个 *.o文件,再对目标文件进行链接和定位,编译成功后会生成一个 *.hex文件和调试文件,接下来要进行调试,如果成功的话,就可以将它固化到 flash 里面去。

启动代码是用来初始化电路以及用来为高级语言写的软件作好运行前准备的一小段汇编语言,是任何处理器上电复位时的程序运行入口点。

比如,刚上电的过程中,PC机会对系统的一个运行频率进行锁定在一个固定的值,这个设计频率的过程就是在汇编源代码中进行的,也就是在启动代码中进行的。与此同时,设置完后,程序开始运行,注意,程序是在内存中运行的。这个时候,就需要把一些源文件从flash里面copy到内存中,又要对它们进行初始化读写,这又有频率的设置。这些都是初始化。

初始化完成后,我们又要设置一些堆栈,要跳到C语言的main函数里面运行。这就需要堆栈。对普通的ARM CPU有这样一个要求:在绝对地址为零的地方要放置一个异常向量表,但并不是所有的ARM CPU都留有这个一个空间,这就需要用到映射的功能。我们可以将其它地方的一些空间映射到绝对地址里面。当发生异常时,ARM核来读取异常中断表的时候,它会使用映射之后的那个表,这个就可以接着往下执行,否则在绝对地址零的地方找不到任何信息,程序就会死掉。这些运行的环境全部建立好后,程序就会跳转到我们的main函数里面。

总之,启动代码,就是对最小系统的初始化。 包括晶振,CPU频率等。

启动代码的最小系统是: 异常向量表的初始化 存储区分配 初始化堆栈 高级语言入口函数调用 – main()函数。

程序的启动过程:

点击看大图

 

 

以下面这个例子为例,编译完后, DEBUG后,我们可以看到,光标指向绝对地址为零的地方,这里存放的就是一个异常向量表。

 

 

它对应在 startup.s 里的源文件如下:

 

点击看大图

 

单步运行后,马上跳转到初始化CPU的频率。即初始化锁相环,将其锁在一个固定的频率。具体代码如下:

; Setup PLL

                IF      PLL_SETUP <> 0

                LDR     R0, =PLL_BASE

                MOV     R1, #0xAA

                MOV     R2, #0x55

 

;  Configure and Enable PLL

                MOV     R3, #PLLCFG_Val

                STR     R3, [R0, #PLLCFG_OFS]

                MOV     R3, #PLLCON_PLLE

                STR     R3, [R0, #PLLCON_OFS]

                STR     R1, [R0, #PLLFEED_OFS]

                STR     R2, [R0, #PLLFEED_OFS]

 

;  Wait until PLL Locked

PLL_Loop        LDR     R3, [R0, #PLLSTAT_OFS]

                ANDS    R3, R3, #PLLSTAT_PLOCK

                BEQ     PLL_Loop

 

;  Switch to PLL Clock

                MOV     R3, #(PLLCON_PLLE:OR:PLLCON_PLLC)

                STR     R3, [R0, #PLLCON_OFS]

                STR     R1, [R0, #PLLFEED_OFS]

                STR     R2, [R0, #PLLFEED_OFS]

                ENDIF   ; PLL_SETUP

 

然后再初始化每一种模式的堆栈,再进行单步运行的时候,下面我们可以看到,它自动跳转到 main()函数:

; Enter the C code

 

                IMPORT  __main

                LDR     R0, =__main

                BX      R0

 

 

                IF      :DEF:__MICROLIB

 

                EXPORT  __heap_base

                EXPORT  __heap_limit

 

                ELSE

 

这个时候,程序会运行各种 scatterload函数,将我们的堆栈、全局变量等内容拷贝到内存中去。拷贝完后,就正式跳转到我们的 main() 函数中来执行了。

 

点击看大图

 

这就是启动代码执行的全过程,呵呵,平时我们看到以为只是执行main()函数就行了,是不是没有想到在执行 main() 函数后还有这么多学问呢?

 

如果觉得对你有有帮助,请顶一顶,这是对我很大的支持!

 

 

系统分类: 单片机
用户分类: STM32
标签: 启动代码 STM32
来源: 原创
发表评论 阅读全文(2679) | 回复(11)

34

关于投票
STM32在RVMDK中入门讲解之开发环境的建立

一般来说,我们可以到RealView 的官方网站上下载最新的的试用版本。网址是: http://www.realview.com.cn 一般来说,开发环境中有很好很强大的示例和帮助文件,如果我们对开发环境十分了解的话,对于我们开发STM32有事半功倍的效果。

一般软件安装的默认目录为:C:\Keil 。而C:\Keil\ARM目录下有这样一些文件夹:

 

点击看大图

 

下面偶来一一讲解每个目录下面各有一些什么东西。

BIN目录下面一般是一些动态链接文件;

BIN31文件夹下面放置的是一些编译器和链接器;

Boards Examples 文件夹下面放置的是一些例程,Boards目录下放置的是根据一些知名厂商所设计的一些开发板的一些例子,而Examples 下面则是一些更大众化的例程。

Flash 文件夹下面放置的是一些厂商的Flash 芯片所用到的驱动算法,我们可以以其中的例程为模板,来添加我们自己的驱动。

GNU目录下面放置的是GNU编译器所使用到的一些资源。

Hlp 目录下面是一些帮助文档,呵呵,3.22版本的帮助文档是经过汉化的,方便极了。

INC 目录下面包含的是一些芯片厂商自己所写的一些库文件,有ADIAtmelSTKOIphilips等等。

RLRT Agent 两个目录下面是一些免费的操作系统,如果我们想写实时操作系统的话,这两个文件夹里面的资料是很好的参考资料哦。

RV31 里面是我们的Rewlview编译器所使用的编译器包含的一些库文件;

Segger Signum 两个目录下面是USB驱动。如果我们使用硬件仿真的时候,使用U-link仿真器的话,如果硬件插上后找不到硬件,那么可以在这里面来重新安装驱动。

Startup 下面放置的是各个芯片厂商的各种启动代码,我们在创建工程的时候编译器会自动提示我们是否要添加启动文件到我们的工程下面。

看完了这些,如果您觉得对您您学习STM32有帮助的话,就帮我顶一顶啊!

 

 

系统分类: 单片机
用户分类: STM32
标签: STM32 开发环境
来源: 原创
发表评论 阅读全文(2221) | 回复(3)

62

关于投票
STM32的LCD驱动及USART驱动

        看了一下STM32 的英文文档,改出了LCD驱动及串口驱动程序,工程建立不多说了,看看这篇文章就一目了然:

http://blog.ednchina.com/likee/137492/message.aspx

 

*----------------------------------------------------------------------------*/

#include <stm32f10x_lib.h>                        // STM32F10x 库定义
#include <stdio.h>
#include "STM32_Init.h"                           // STM32 初始化

/*----------------------------------------------------------------------------
  Notes:
  The length of the receive and transmit buffers must be a power of 2.
  Each buffer has a next_in and a next_out index.
  If next_in = next_out, the buffer is empty.
  (next_in - next_out) % buffer_size = the number of characters in the buffer.
 *----------------------------------------------------------------------------*/
#define TBUF_SIZE   256      /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
#define RBUF_SIZE   256      /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/

/*----------------------------------------------------------------------------
 *----------------------------------------------------------------------------*/
#if TBUF_SIZE < 2
#error TBUF_SIZE is too small.  It must be larger than 1.
#elif ((TBUF_SIZE & (TBUF_SIZE-1)) != 0)
#error TBUF_SIZE must be a power of 2.
#endif

#if RBUF_SIZE < 2
#error RBUF_SIZE is too small.  It must be larger than 1.
#elif ((RBUF_SIZE & (RBUF_SIZE-1)) != 0)
#error RBUF_SIZE must be a power of 2.
#endif

/*----------------------------------------------------------------------------
 *----------------------------------------------------------------------------*/
struct buf_st {
  unsigned int in;                                // Next In Index
  unsigned int out;                               // Next Out Index
  char buf [RBUF_SIZE];                           // 缓冲区
};

static struct buf_st rbuf = { 0, 0, };
#define SIO_RBUFLEN ((unsigned short)(rbuf.in - rbuf.out))

static struct buf_st tbuf = { 0, 0, };
#define SIO_TBUFLEN ((unsigned short)(tbuf.in - tbuf.out))

static unsigned int tx_restart = 1;               // 如果发送重新启动

/*----------------------------------------------------------------------------
  USART1_IRQHandler
  Handles USART1 global interrupt request.
 *----------------------------------------------------------------------------*/
void USART1_IRQHandler (void) {
  volatile unsigned int IIR;
  struct buf_st *p;

    IIR = USART1->SR;
    if (IIR & USART_FLAG_RXNE) {                  // 如果读中断
      USART1->SR &= ~USART_FLAG_RXNE;           // 则清除中断

      p = &rbuf;

      if (((p->in - p->out) & ~(RBUF_SIZE-1)) == 0) {
        p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);
        p->in++;
      }
    }

    if (IIR & USART_FLAG_TXE) {       //发送中断
      USART1->SR &= ~USART_FLAG_TXE;           // 清中断

      p = &tbuf;

      if (p->in != p->out) {
        USART1->DR = (p->buf [p->out & (TBUF_SIZE-1)] & 0x1FF);
        p->out++;
        tx_restart = 0;
      }
      else {
        tx_restart = 1;
  USART1->CR1 &= ~USART_FLAG_TXE;        // disable TX interrupt if nothing to send

      }
    }
}

/*------------------------------------------------------------------------------
  buffer_Init
  initialize the buffers
 *------------------------------------------------------------------------------*/
void buffer_Init (void) {

  tbuf.in = 0;                                    //清除缓冲区索引
  tbuf.out = 0;
  tx_restart = 1;

  rbuf.in = 0;
  rbuf.out = 0;
}

/*------------------------------------------------------------------------------
  SenChar
  transmit a character
 *------------------------------------------------------------------------------*/
int SendChar (int c) {
  struct buf_st *p = &tbuf;

                                                  //如果缓冲区满了。返回错误
  if (SIO_TBUFLEN >= TBUF_SIZE)
    return (-1);
                                                 
  p->buf [p->in & (TBUF_SIZE - 1)] = c;           //将数据加放发送缓冲氏
  p->in++;

  if (tx_restart) {                               //如果发送中断不能使,则使其能使
    tx_restart = 0;
 USART1->CR1 |= USART_FLAG_TXE;            // 允许发送中断
  }

  return (0);
}

/*------------------------------------------------------------------------------
  GetKey
  receive a character
 *------------------------------------------------------------------------------*/
int GetKey (void) {
  struct buf_st *p = &rbuf;

  if (SIO_RBUFLEN == 0)
    return (-1);

  return (p->buf [(p->out++) & (RBUF_SIZE - 1)]);
}


/*----------------------------------------------------------------------------
  MAIN function
 *----------------------------------------------------------------------------*/
int main (void) {
  int  i;
  char c;

  buffer_Init();                                  // init RX / TX buffers
  stm32_Init ();                                  // STM32 setup

  lcdWrStr("KEIL");
  for (i = 0; i < 10000; i++);                 // Wait for initial display
  printf ("\r\n Interrupt driven Serial I/O Example \r\n");

  lcdWrStr("->  ");
  while (1) {                                     // Loop forever
    printf ("Please press a key. ");
    c = getchar ();
    printf ("\r\n");
    printf ("You pressed just now is  '%c'.\r\n\r\n", c);

    lcdWrPos(&c, 3);

  } // end while
} // end main

 

#include <stm32f10x_lib.h>                              // stm32f10x definitions
#include "STM32_Reg.h"                                  // missing bit definitions


unsigned short lcdChar[128] = {
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, //   0..9    unused
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, //  10..19   unused
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, //  20..29   unused
  0x0000, 0x0000, 0x0000, 0x2002, 0x0012, 0x0000, 0xAE16, 0x0000, 0x0000, 0x0002, //  30..39   unused          
  0x0000, 0x0000, 0x7662, 0x2602, 0x2000, 0x0600, 0x0000, 0x1040, 0x99D4, 0x08C0, //  40..47   unused, '0', '1'          
  0x8784, 0x8C84, 0x0E90, 0x8E14, 0x8F14, 0x1044, 0x8F94, 0x8E94, 0x0000, 0x0000, //  '2'..'9', 58..59 unused          
  0x4040, 0x8600, 0x1020, 0x2494, 0x0000, 0x0F94, 0x8F10, 0x8114, 0x8F80, 0x8314, //  60..65   unused, 'A'..'E'          
  0x0314, 0x8D14, 0x0F90, 0x2002, 0x4880, 0x4350, 0x8110, 0x09F0, 0x49B0, 0x8994, //  'F'..'O'          
  0x0794, 0xC994, 0x4394, 0x8E14, 0x2006, 0x8990, 0x8990, 0x5990, 0x5060, 0x0160, //  'P'..'Y'         
  0x9044, 0x8114, 0x4020, 0x8884, 0x0000, 0x8000, 0x0010, 0x0F94, 0x8F10, 0x8700, //  'Z', 91..96 unused, 'a'..'c'          
  0x8F80, 0x8314, 0x0314, 0x8D14, 0x0F90, 0x2002, 0x4880, 0x4350, 0x8110, 0x09F0, //  'e'..'m'          
  0x49B0, 0x8F00, 0x0794, 0xC994, 0x0300, 0x8E14, 0x2006, 0x8990, 0x8990, 0x5900, //  'n'..'w'          
  0x5060, 0x0160, 0x9044, 0x0000, 0x2002, 0x0000, 0x0000, 0x0000};                //  'x'..'z', 123..127 unused          

 

unsigned short lcdBuffer[4] = {0, 0, 0, 0};             // LCD representation
unsigned int   lcdState     = 0;                        // write or clear
unsigned int   lcdWrState   = 0;                        // normal or inverted
unsigned int   lcdLine      = 0;                        // line 0..4


/*----------------------------------------------------------------------------
  clear LCD
 *----------------------------------------------------------------------------*/
void lcdClr (void) {
  int lineNr;

  for (lineNr = 0; lineNr < 4; lineNr++) {
    lcdBuffer[lineNr] = 0;
  }

}

/*----------------------------------------------------------------------------
  write a character to LCD position (0..3). Position 0 is left, 3 is right
 *----------------------------------------------------------------------------*/
void lcdWrPos (char* c, int pos) {
  int            lineNr;
  unsigned short cLcd;                                  // character coding for LCD

  cLcd = lcdChar[*c & 0x7F];                            // we only handel character 0..127

  for (lineNr = 0; lineNr < 4; lineNr++) {
    lcdBuffer[lineNr] = (lcdBuffer[lineNr] & ~(0xF<<(4*pos))) | (((cLcd>>(lineNr*4))& 0xF)<<(4*pos));
  }

}

/*----------------------------------------------------------------------------
  write a string to LCD position (0..3). max chars written is 4
 *----------------------------------------------------------------------------*/
void lcdWrStr (char* s) {
  int i;

  for (i = 0; i < 4; i++) {
    lcdWrPos (s+i, i);
  }

}


/*----------------------------------------------------------------------------
  TIM4_IRQHandler
  ticks every 2ms
 *----------------------------------------------------------------------------*/
void TIM4_IRQHandler(void)
{
  if (TIM4->SR & (1<<0)) {                              // UIF set?
//--------------------------------
  GPIOC->CRH &= 0xFFFF0000;
  GPIOC->CRH |= 0x00004444;                             // COM1..4 input floating
  GPIOC->CRH &= ~(0xF<<(4*(3-lcdLine)));
  GPIOC->CRH |=  (0x3<<(4*(3-lcdLine)));                // COMx    ouput push pull 50 MHz

  switch (lcdState){
    case 0:                                             // write state
      switch (lcdWrState) {
        case 0:                                         // write nomal data stat
          GPIOE->ODR  = lcdBuffer[lcdLine];             // write data
          GPIOC->BRR  = (1<<(8+(3-lcdLine)));           // COMx low

          lcdWrState = 1;                               // next state is write inverted data                      
        break;
        case 1:                                         // write inverted data state
          GPIOE->ODR  = ~lcdBuffer[lcdLine];            // write inverted data
          GPIOC->BSRR = (1<<(8+(3-lcdLine)));           // COMx high

          lcdWrState = 0;                               // next state is write normal data
          lcdLine = ((lcdLine+1) == 4) ? 0 : lcdLine+1; // handle next COM
        break;
      } // end switch (lcdWrState)

      lcdState = 1;                                     // next state is clearState
    break;

    case 1:                                             // clear state
      GPIOC->CRH &= 0xFFFF0000;
      GPIOC->CRH |= 0x00003333;                         // COM1..4 ouput push pull 50 MHz

      GPIOE->ODR  =  0x0000;                            // all Segments off
      GPIOC->BRR  = (0xF<<8);                           // all Lines off

      lcdState = 0;                                     // next state is writeState
    break;

  } // end switch (lcdState)
//--------------------------------
    TIM4->SR &= ~(1<<0);                                // clear UIF flag
  }

}

 

纯软件仿真结果如下:

是不是很爽呢?呵呵。

 

 

系统分类: 单片机
用户分类: STM32
标签: USART LCD
来源: 原创
发表评论 阅读全文(2715) | 回复(7)
总共 , 当前 /