STM32_2

  STM32时钟控制RCC



对于单片机系统来说,CPU和总线以及外设的时钟设置是非常重要的,因为没有时钟就没有时序,组合电路能干什么想必各位心里都清楚。其实时钟的学习这部分应该提前一些,但由于一开始时间比较短,有些急于求成,所以直接使用了万利给的例程,姑且跳过了这一步。介于下面我计划要学习的任务都涉及到兆级的高速传输,例如全速USB,DMA等等,所以不能再忽略时钟啦,必须要仔细研究一下。

我学习RCC的参考资料:
技术文档0427及其中文翻译版STM32F10xxx_Library_Manual_ChineseV2的第十五章和RM0008_CH参考手册。

准备知识:
片上总线标准种类繁多,而由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。二者分别适用于高速与相对低速设备的连接。


由于时钟是一个由内而外的东西,具体设置要从寄存器开始。

RCC 寄存器结构,RCC_TypeDeff,在文件“stm32f10x_map.h”中定义如下: 
typedef struct 

vu32 CR; 
vu32 CFGR; 
vu32 CIR; 
vu32 APB2RSTR; 
vu32 APB1RSTR; 
vu32 AHBENR; 
vu32 APB2ENR; 
vu32 APB1ENR; 
vu32 BDCR; 
vu32 CSR; 
} RCC_TypeDef;

这些寄存器的具体定义和使用方式参见芯片手册,在此不赘述,因为C语言的开发可以不和他们直接打交道,当然如果能够加以理解和记忆,无疑是百利而无一害。

相信细心的朋友早就发现板子上只有8Mhz的晶振,而增强型最高工作频率为72Mhz,显然需要用PLL倍频9倍,这些设置都需要在初始化阶段完成。为了方便说明,我借用一下例程的RCC设置函数,并用中文注释的形式加以说明:

/*******************************************************************************
*  Name : Set_System
* Deion    : Configures Main system clocks & power
* Input          : None.
* Return         : None.
*******************************************************************************/
//在此指出上面的注释头应该是复制过来的,写错了...不过没关系,反正没参数需要说明,重要的是函数体。
static void RCC_Config(void)
{

/* 这里是重置了RCC的设置,类似寄存器复位 */
RCC_DeInit();

/* 使能外部高速晶振 */
RCC_HSEConfig(RCC_HSE_ON);

/* 等待高速晶振稳定 */
HSEStartUpStatus = RCC_WaitForHSEStartUp();

if (HSEStartUpStatus == SUCCESS)
{
    /* 使能flash预读取缓冲区 */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* 令Flash处于等待状态,2是针对高频时钟的,这两句跟RCC没直接关系,可以暂且略过 */
    FLASH_SetLatency(FLASH_Latency_2);

    /* HCLK = SYSCLK 设置高速总线时钟=系统时钟*/
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* PCLK2 = HCLK 设置低速总线2时钟=高速总线时钟*/
    RCC_PCLK2Config(RCC_HCLK_Div1);

    /* PCLK1 = HCLK/2 设置低速总线1的时钟=高速时钟的二分频*/
    RCC_PCLK1Config(RCC_HCLK_Div2);

    /* ADCCLK = PCLK2/6 设置ADC外设时钟=低速总线2时钟的六分频*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* Set PLL clock output to 72MHz using HSE (8MHz) as entry clock */ 
    //上面这句例程中缺失了,但却很关键
    /* 利用锁相环讲外部8Mhz晶振9倍频到72Mhz */ 
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); 

    /* Enable PLL 使能锁相环*/
    RCC_PLLCmd(ENABLE); 


    /* Wait till PLL is ready 等待锁相环输出稳定*/
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {}

    /* Select PLL as system clock source 将锁相环输出设置为系统时钟 */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source 等待校验成功*/
    while (RCC_GetSYSCLKSource() != 0x08)
    {}
}

    /* Enable FSMC, GPIOD, GPIOE, GPIOF, GPIOG and AFIO clocks */
    //使能外围接口总线时钟,注意各外设的隶属情况,不同芯片的分配不同,到时候查手册就可以
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |
                         RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG |
                         RCC_APB2Periph_AFIO, ENABLE);
}


由上述程序可以看出系统时钟的设定是比较复杂的,外设越多,需要考虑的因素就越多。同时这种设定也是有规律可循的,设定参数也是有顺序规范的,这是应用中应当注意的,例如PLL的设定需要在使能之前,一旦PLL使能后参数不可更改。

经过此番设置后,由于我的电路板上是8Mhz晶振,所以系统时钟为72Mhz,高速总线和低速总线2都为72Mhz,低速总线1为36Mhz,ADC时钟为12Mhz,USB时钟经过1.5分频设置就可以实现48Mhz的数据传输。

一般性的时钟设置需要先考虑系统时钟的来源,是内部RC还是外部晶振还是外部的振荡器,是否需要PLL。然后考虑内部总线和外部总线,最后考虑外设的时钟信号。遵从先倍频作为CPU时钟,然后在由内向外分频,下级迁就上级的原则有点儿类似PCB制图的规范化要求,在这里也一样。


  (原文件名:STM32系统时钟框图.gif) 




                                                    STM32的时钟系统分析



在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。 
  ①、HSI是高速内部时钟,RC振荡器,频率为8MHz。

  ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。

  ③、LSI是低速内部时钟,RC振荡器,频率为40kHz。

  ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。

  ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

  其中40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外,实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。

  STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。

  另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。

  系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。系统时钟可选择为PLL输出、HSI或者HSE。系统时钟最大频率为72MHz,它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:

  ①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。

  ②、通过8分频后送给Cortex的系统定时器时钟。

  ③、直接送给Cortex的空闲运行时钟FCLK。

  ④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。

  ⑤、送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

  在以上的时钟输出中,有很多是带使能控制的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。

  需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。

  连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。

  连接在APB2(高速外设)上的设备有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。

/*******************************************************************************
*  Name : RCC_Configuration
* Deion    : Configures the different system clocks.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;

/* RCC system reset(for debug purpose) */
// RCC_DeInit();

/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);

/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();

if(HSEStartUpStatus == SUCCESS)
{
    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);

    /* ADCCLK = PCLK2/6 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);

    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* PLLCLK = 8MHz * 9 = 72 MHz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//Pll在最后设置

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
}

/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
         | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);

/* TIM2 clocks enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

/* CAN Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
}
                                      


                                     STM32的学习心得之TIMx(通用定时器)
基本的配置定时器的基本设置 
1、TIM_TimeBaseStructure.TIM_Prescaler = 0x0;//时钟预分频数 例如:时钟频率=72/(时钟预分频+1) 
2、TIM_TimeBaseStructure.TIM_CounterMode = TIM1_CounterMode_Up; //定时器模式 向上计数 
3、TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // 自动重装载寄存器周期的值(定时时间) 累计 0xFFFF个频率后产生个更新或者中断(也是说定时时间到)
4、TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时间分割值 
5、TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//初始化定时器2

6、TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //打开中断 溢出中断 

7、TIM_Cmd(TIM2, ENABLE);//打开定时器

此外要记住一定要打开定时器的时钟(RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);),定时器的频率的可以编程的,有对应的模式设置和中断处理。



                                                STM32学习笔记之 串口通讯


主要功能是把PC机发送的数据接收后再返回给PC机参数9600,8,1,N。

/************************************************************************
     Copyright (c) 2008 wormchen             
     All rights reserved             
文 件 名: main.c              
说    明: 串口发送接收数据 将PC端发来的数据返回给PC          
主要硬件: EMSTM32V1+miniSTMV100(外部8MRC)         
编译环境: MDK3.10              
当前版本: 1.0                      
************************************************************************/ 
#i nclude 


void RCC_Config(void);
void GPIO_Config(void);
void USART_Config(void);
void Put_String(u8 *p);

int main(void)
{
RCC_Config();
GPIO_Config();
USART_Config();
Put_String("\r\n请发送数据_\r\n");
while(1)

   while(1)  
   { 
    if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET)
    {
      
      USART_SendData(USART2, USART_ReceiveData(USART2));
    }
   } 
}
}
/*************************************************
函数: void RCC_Config(void)
功能: 配置系统时钟 
参数: 无
返回: 无
**************************************************/
void RCC_Config(void)
{
ErrorStatus HSEStartUpStatus;//定义外部高速晶体启动状态枚举变量
RCC_DeInit();//复位RCC外部设备寄存器到默认值
RCC_HSEConfig(RCC_HSE_ON); //打开外部高速晶振
HSEStartUpStatus = RCC_WaitForHSEStartUp();//等待外部高速时钟准备好
if(HSEStartUpStatus == SUCCESS)//外部高速时钟已经准别好
   {

    RCC_HCLKConfig(RCC_SYSCLK_Div1);//配置AHB(HCLK)时钟等于==SYSCLK
    RCC_PCLK2Config(RCC_HCLK_Div1); //配置APB2(PCLK2)钟==AHB时钟
    RCC_PCLK1Config(RCC_HCLK_Div2);//配置APB1(PCLK1)钟==AHB1/2时钟
     
      
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    //配置PLL时钟 == 外部高速晶体时钟*9

      RCC_PLLCmd(ENABLE);//使能PLL时钟
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) //等待PLL时钟就绪
       {
       }
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//配置系统时钟 = PLL时钟
  
      while(RCC_GetSYSCLKSource() != 0x08) //检查PLL时钟是否作为系统时钟
       {
       }
   }
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
   //打开GPIOD和AFIO时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//使能串口2时钟
}
/*************************************************
函数: void GPIO_Config(void)
功能: GPIO配置 
参数: 无
返回: 无
**************************************************/
void GPIO_Config(void)
{
//设置RTS(PD.04),Tx(PD.05)为推拉输出模式
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化结构体
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);//使能GPIO端口映射USART2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;//选择PIN4 PIN5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //引脚频率50M
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//引脚设置推拉输出
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化GPIOD

//配置CTS (PD.03),USART2 Rx (PD.06)为浮点输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
/*************************************************
函数: void USART_Config(void)
功能: USART配置 
参数: 无
返回: 无
**************************************************/
void USART_Config(void)
{
USART_InitTypeDef USART_InitStructure; //定义串口初始化结构体
USART_InitStructure.USART_BaudRate = 9600;//波特率9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据 
USART_InitStructure.USART_StopBits = USART_StopBits_1;//1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No ; //无校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//禁用RTSCTS硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//使能发送接收
USART_InitStructure.USART_Clock = USART_Clock_Disable; //串口时钟禁止
USART_InitStructure.USART_CPOL = USART_CPOL_Low; //时钟下降沿有效
USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;//数据在第二个时钟沿捕捉
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
//最后数据位的时钟脉冲不输出到SCLK引脚
USART_Init(USART2, &USART_InitStructure);//初始化串口2
USART_Cmd(USART2, ENABLE);//串口2使能
}
/*************************************************
函数: void Put_String(void)
功能: 向串口输出字符串
参数: 无
返回: 无
**************************************************/
void Put_String(u8 *p)
{
while(*p)
{
   USART_SendData(USART2, *p++);
   while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
   {
   
   }
}
}




                                                       基于STM32的PWM输出
                                                   


) 初始化函数定义:
void TIM_Configuration(void);   //定义TIM初始化函数
d) 初始化函数调用:
TIM_Configuration();   //TIM初始化函数调用
e) 初始化函数,不同于前面模块,TIM的初始化分为两部分——基本初始化和通道初始化:
void TIM_Configuration(void)//TIM初始化函数

   TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;//定时器初始化结构
   TIM_OCInitTypeDef   TIM_OCInitStructure;//通道输出初始化结构

//TIM3初始化
   TIM_TimeBaseStructure.TIM_Period = 0xFFFF;        //周期0~FFFF
   TIM_TimeBaseStructure.TIM_Prescaler = 5;       //时钟分频
   TIM_TimeBaseStructure.TIM_ClockDivision = 0;    //时钟分割
   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//模式
   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //基本初始化
   TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE);//打开中断,中断需要这行代码
  
//TIM3通道初始化
   TIM_OCStructInit(& TIM_OCInitStructure);                                              //默认参数
   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                    //工作状态
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;       //设定为输出,需要PWM输出才需要这行代码
   TIM_OCInitStructure.TIM_Pulse = 0x2000;                   //占空长度
   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                 //高电平
   TIM_OC4Init(TIM3, &TIM_OCInitStructure);                   //通道初始化

   TIM_Cmd(TIM3, ENABLE);                                                                      //启动TIM3
}

f) RCC初始化函数中加入TIM时钟开启:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3, ENABLE);
g) GPIO里面将输入和输出管脚模式进行设置。信号:AF_PP,50MHz。
h) 使用中断的话在NVIC里添加如下代码:

//打开TIM2中断
   NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;   //通道
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//占先级
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;           //响应级
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       //启动
   NVIC_Init(&NVIC_InitStructure);                                            //初始化

中断代码:
void TIM2_IRQHandler(void)
{
   if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)    //判断中断来源
   {
TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);          //清除中断标志
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_11)));//变换LED色彩
IC4value = TIM_GetCapture4(TIM2);                //获取捕捉数值
   }  
}

i) 简单应用:
//改变占空比
TIM_SetCompare4(TIM3, 变量);

j) 注意事项:
管脚的IO输出模式是根据应用来定,比如如果用PWM输出驱动LED则应该将相应管脚设为AF_PP,否则单片机没有输出。








                                               STM32资料一(转载)





注:下面是一些常用的代码,网上很多但是大多注释不全。高手看没问题,对于我们这些新手就费劲了……所以我把这些代码集中,进行了逐句注释,希望对新手们有价值。

flash: 芯片内部存储器flash操作函数

我的理解——对芯片内部flash进行操作的函数,包括读取,状态,擦除,写入等等,可以允许程序去操作flash上的数据。

基础应用1,FLASH时序延迟几个周期,等待总线同步操作。推荐按照单片机系统运行频率,0—24MHz时,取Latency=0;24—48MHz时,取Latency=1;48~72MHz时,取Latency=2。所有程序中必须的

用法:FLASH_SetLatency(FLASH_Latency_2);

位置:RCC初始化子函数里面,时钟起振之后。

基础应用2,开启FLASH预读缓冲功能,加速FLASH的读取。所有程序中必须的

用法:FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

位置:RCC初始化子函数里面,时钟起振之后。

3、         lib:调试所有外设初始化的函数。

我的理解——不理解,也不需要理解。只要知道所有外设在调试的时候,EWRAM需要从这个函数里面获得调试所需信息的地址或者指针之类的信息。

基础应用1,只有一个函数debug。所有程序中必须的。

用法:        #ifdef DEBUG

               debug();

#endif

       位置:main函数开头,声明变量之后。

4、         nvic:系统中断管理。

我的理解——管理系统内部的中断,负责打开和关闭中断。

基础应用1,中断的初始化函数,包括设置中断向量表位置,和开启所需的中断两部分。所有程序中必须的。

用法:        void NVIC_Configuration(void)

{

NVIC_InitTypeDef NVIC_InitStructure;                   //中断管理恢复默认参数

#ifdef   VECT_TAB_RAM   //如果C/C++ Compiler\Preprocessor\Defined symbols中的定义了VECT_TAB_RAM(见程序库更改内容的表格)

NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //则在RAM调试

#else                                                                   //如果没有定义VECT_TAB_RAM

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//则在Flash里调试

#endif                                                                   //结束判断语句

//以下为中断的开启过程,不是所有程序必须的。

//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//设置NVIC优先级分组,方式。

//注:一共16个优先级,分为抢占式和响应式。两种优先级所占的数量由此代码确定,NVIC_PriorityGroup_x可以是0、1、2、3、4,分别代表抢占优先级有1、2、4、8、16个和响应优先级有16、8、4、2、1个。规定两种优先级的数量后,所有的中断级别必须在其中选择,抢占级别高的会打断其他中断优先执行,而响应级别高的会在其他中断执行完优先执行。

//NVIC_InitStructure.NVIC_IRQChannel = 中断通道名; //开中断,中断名称见函数库

//NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级

//NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         //响应优先级

//NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动此通道的中断

//NVIC_Init(&NVIC_InitStructure);                                     //中断初始化

}

5、         rcc:单片机时钟管理。

我的理解——管理外部、内部和外设的时钟,设置、打开和关闭这些时钟。

基础应用1:时钟的初始化函数过程——

用法:void RCC_Configuration(void)                                //时钟初始化函数

{

   ErrorStatus HSEStartUpStatus;                                     //等待时钟的稳定

   RCC_DeInit();                                                                //时钟管理重置

   RCC_HSEConfig(RCC_HSE_ON);                                  //打开外部晶振

   HSEStartUpStatus = RCC_WaitForHSEStartUp();          //等待外部晶振就绪

if (HSEStartUpStatus == SUCCESS)

   {

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

//flash读取缓冲,加速

FLASH_SetLatency(FLASH_Latency_2);                      //flash操作的延时

RCC_HCLKConfig(RCC_SYSCLK_Div1);                       //AHB使用系统时钟

RCC_PCLK2Config(RCC_HCLK_Div2);                          //APB2(高速)为HCLK的一半

RCC_PCLK1Config(RCC_HCLK_Div2);                          //APB1(低速)为HCLK的一半

//注:AHB主要负责外部存储器时钟。PB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。

RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MH

RCC_PLLCmd(ENABLE);                                              //启动PLL

while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}     //等待PLL启动

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //将PLL设置为系统时钟源

while (RCC_GetSYSCLKSource() != 0x08){}     //等待系统时钟源的启动

   }

//RCC_AHBPeriphClockCmd(ABP2设备1 | ABP2设备2 |, ENABLE); //启动AHP设备

//RCC_APB2PeriphClockCmd(ABP2设备1 | ABP2设备2 |, ENABLE);//启动ABP2设备

   //RCC_APB1PeriphClockCmd(ABP2设备1 | ABP2设备2 |, ENABLE); //启动ABP1设备

}

6、        exti:外部设备中断函数

我的理解——外部设备通过引脚给出的硬件中断,也可以产生软件中断,19个上升、下降或都触发。EXTI0~EXTI15连接到管脚,EXTI线16连接到PVD(VDD监视),EXTI线17连接到RTC(闹钟),EXTI线18连接到USB(唤醒)。

基础应用1,设定外部中断初始化函数。按需求,不是必须代码。

用法: void EXTI_Configuration(void)

{

EXTI_InitTypeDef EXTI_InitStructure;                               //外部设备中断恢复默认参数

EXTI_InitStructure.EXTI_Line = 通道1|通道2; //设定所需产生外部中断的通道,一共19个。

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;     //产生中断

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升下降沿都触发

EXTI_InitStructure.EXTI_LineCmd = ENABLE;             //启动中断的接收

EXTI_Init(&EXTI_InitStructure);             //外部设备中断启动

}

7、        dma:通过总线而越过CPU读取外设数据

我的理解——通过DMA应用可以加速单片机外设、存储器之间的数据传输,并在传输期间不影响CPU进行其他事情。这对于入门开发基本功能来说没有太大必要,这个内容先行跳过。

8、        systic:系统定时器

我的理解——可以输出和利用系统时钟的计数、状态。

基础应用1,精确计时的延时子函数。推荐使用的代码。

用法:

static vu32 TimingDelay;                                                                               //全局变量声明

void SysTick_Config(void)                                                                            //systick初始化函数

{

SysTick_CounterCmd(SysTick_Counter_Disable);                                  //停止系统定时器

SysTick_ITConfig(DISABLE); //停止systick中断

    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //systick使用HCLK作为时钟源,频率值除以8。

SysTick_SetReload(9000);                                                                       //重置时间1毫秒(以72MHz为基础计算)

SysTick_ITConfig(ENABLE);                                                                   //开启systic中断

}

void Delay (u32 nTime)                                                                                   //延迟一毫秒的函数

{

SysTick_CounterCmd(SysTick_Counter_Enable);                      //systic开始计时

   TimingDelay = nTime;                      //计时长度赋值给递减变量

while(TimingDelay != 0); //检测是否计时完成

SysTick_CounterCmd(SysTick_Counter_Disable);                                       //关闭计数器

SysTick_CounterCmd(SysTick_Counter_Clear);                      //清除计数值

}

void TimingDelay_Decrement(void) //递减变量函数,函数名由“stm32f10x_it.c”中的中断响应函数定义好了。

{

if (TimingDelay != 0x00) //检测计数变量是否达到0

{ TimingDelay--; //计数变量递减

}

}

注:建议熟练后使用,所涉及知识和设备太多,新手出错的可能性比较大。新手可用简化的延时函数代替:

void Delay(vu32 nCount)                                                                               //简单延时函数

{

   for(; nCount != 0; nCount--);                                                                          //循环变量递减计数

}

当延时较长,又不需要精确计时的时候可以使用嵌套循环:

void Delay(vu32 nCount)                      //简单的长时间延时函数

{int i; //声明内部递减变量

   for(; nCount != 0; nCount--)                                                             //递减变量计数

{for (i=0; i<0xffff; i++)}                                                                                  //内部循环递减变量计数

}

9、        gpio:I/O设置函数

我的理解——所有输入输出管脚模式设置,可以是上下拉、浮空、开漏、模拟、推挽模式,频率特性为2M,10M,50M。也可以向该管脚直接写入数据和读取数据。

基础应用1,gpio初始化函数。所有程序必须。

用法:void GPIO_Configuration(void)

{

GPIO_InitTypeDef GPIO_InitStructure;                      //GPIO状态恢复默认参数

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_标号 | GPIO_Pin_标号 ; //管脚位置定义,标号可以是NONE、ALL、0至15。

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//输出速度2MHz

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入模式

GPIO_Init(GPIOC, &GPIO_InitStructure); //C组GPIO初始化

//注:以上四行代码为一组,每组GPIO属性必须相同,默认的GPIO参数为:ALL,2MHz,FLATING。如果其中任意一行与前一组相应设置相同,那么那一行可以省略,由此推论如果前面已经将此行参数设定为默认参数(包括使用GPIO_InitTypeDef GPIO_InitStructure代码),本组应用也是默认参数的话,那么也可以省略。以下重复这个过程直到所有应用的管脚全部被定义完毕。

……

}

基础应用2,向管脚写入0或1

用法:GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);    //写入1

基础应用3,从管脚读入0或1

用法:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)


                                                                                                           让它跑起来,基本硬件功能的建立

0、 实验之前的准备

a) 接通串口转接器

b) 下载IO与串口的原厂程序,编译通过保证调试所需硬件正常。

1、 flash,lib,nvic,rcc和GPIO,基础程序库编写

a) 这几个库函数中有一些函数是关于芯片的初始化的,每个程序中必用。为保障程序品质,初学阶段要求严格遵守官方习惯。注意,官方程序库例程中有个platform_config.h文件,是专门用来指定同类外设中第几号外设被使用,就是说在main.c里面所有外设序号用x代替,比如USARTx,程序会到这个头文件中去查找到底是用那些外设,初学的时候参考例程别被这个所迷惑住。

b) 全部必用代码取自库函数所带例程,并增加逐句注释。

c) 习惯顺序——Lib(debug),RCC(包括Flash优化),NVIC,GPIO

d) 必用模块初始化函数的定义:

void RCC_Configuration(void); //定义时钟初始化函数

void GPIO_Configuration(void); //定义管脚初始化函数

void NVIC_Configuration(void); //定义中断管理初始化函数

void Delay(vu32 nCount); //定义延迟函数

e) Main中的初始化函数调用:

RCC_Configuration(); //时钟初始化函数调用

NVIC_Configuration(); //中断初始化函数调用

GPIO_Configuration(); //管脚初始化函数调用

f) Lib注意事项:

属于Lib的Debug函数的调用,应该放在main函数最开始,不要改变其位置。

g) RCC注意事项:

Flash优化处理可以不做,但是两句也不难也不用改参数……

根据需要开启设备时钟可以节省电能

时钟频率需要根据实际情况设置参数

h) NVIC注意事项

注意理解占先优先级和响应优先级的分组的概念

i) GPIO注意事项

注意以后的过程中收集不同管脚应用对应的频率和模式的设置。

作为高低电平的I/O,所需设置:RCC初始化里面打开RCC_APB2

PeriphClockCmd(RCC_APB2Periph_GPIOA);GPIO里面管脚设定:IO输出(50MHz,Out_PP);IO输入(50MHz,IPU);

j) GPIO应用

GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_RESET);//重置

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//写入1

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//写入0

GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) ;//读入IO

k) 简单Delay函数

void Delay(vu32 nCount)//简单延时函数

{for(; nCount != 0; nCount--);}

实验步骤:

RCC初始化函数里添加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB , ENABLE);

不用其他中断,NVIC初始化函数不用改

GPIO初始化代码:

//IO输入,GPIOB的2、10、11脚输出

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;//管脚号

   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度

   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输入输出模式

    GPIO_Init(GPIOB, &GPIO_InitStructure);              //初始化

简单的延迟函数:

void Delay(vu32 nCount)                   //简单延时函数

{ for (; nCount != 0; nCount--);}           //循环计数延时

完成之后再在main.c的while里面写一段:

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//写入1

Delay(0xffff);

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//写入0

Delay(0xffff);

就可以看到连接在PB2脚上的LED闪烁了,单片机就跑起来了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值