text: ﻿#include "sys.h"
  //////////////////////////////////////////////////////////////////////////////////
  //本程序只供学习使用，未经作者许可，不得用于其它任何用途
  //ALIENTEK STM32开发板
  //系统时钟初始化（适合STM32F10x系列）
  //正点原子@ALIENTEK
  //技术论坛:www.openedv.com
  //创建日期:2010/1/1
  //版本：V1.9
  //版权所有，盗版必究。
  //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  //All rights reserved
  //********************************************************************************
  //V1.4修改说明
  //把NVIC KO了,没有使用任何库文件!
  //加入了JTAG_Set函数
  //V1.5 20120322
  //增加void INTX_DISABLE(void)和void INTX_ENABLE(void)两个函数
  //V1.6 20120412
  //1,增加MSR_MSP函数
  //2,修改VECT_TAB_RAM的默认偏移,设置为0.
  //V1.7 20120818
  //1,添加ucos支持配置宏SYSTEM_SUPPORT_UCOS
  //2,修改了注释
  //3,去掉了不常用函数BKP_Write
  //V1.8 20131120
  //1,修改头文件为stm32f10x.h,不再使用stm32f10x_lib.h及其相关头文件
  //V1.9 20150109
  //1,修改头文件为MY_NVIC_Init函数部分代码以支持向量号大于63的中断的设置
  //2,修改WFI_SET/INTX_DISABLE/INTX_ENABLE等函数的实现方式
  //V2.0 20150322
  //修改SYSTEM_SUPPORT_UCOS为SYSTEM_SUPPORT_OS
  //////////////////////////////////////////////////////////////////////////////////
  
  //设置向量表偏移地址
  //NVIC_VectTab:基址
  //Offset:偏移量
  void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
  {
  	SCB->VTOR = NVIC_VectTab | (Offset & (u32)0x1FFFFF80); //设置NVIC的向量表偏移寄存器
  														   //用于标识向量表是在CODE区还是在RAM区
  }
  //设置NVIC分组
  //NVIC_Group:NVIC分组 0~4 总共5组
  void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
  {
  	u32 temp, temp1;
  	temp1 = (~NVIC_Group) & 0x07; //取后三位
  	temp1 <<= 8;
  	temp = SCB->AIRCR;  //读取先前的设置
  	temp &= 0X0000F8FF; //清空先前分组
  	temp |= 0X05FA0000; //写入钥匙
  	temp |= temp1;
  	SCB->AIRCR = temp; //设置分组
  }
  //设置NVIC
  //NVIC_PreemptionPriority:抢占优先级
  //NVIC_SubPriority       :响应优先级
  //NVIC_Channel           :中断编号
  //NVIC_Group             :中断分组 0~4
  //注意优先级不能超过设定的组的范围!否则会有意想不到的错误
  //组划分:
  //组0:0位抢占优先级,4位响应优先级
  //组1:1位抢占优先级,3位响应优先级
  //组2:2位抢占优先级,2位响应优先级
  //组3:3位抢占优先级,1位响应优先级
  //组4:4位抢占优先级,0位响应优先级
  //NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
  void MY_NVIC_Init(u8 NVIC_PreemptionPriority, u8 NVIC_SubPriority, u8 NVIC_Channel, u8 NVIC_Group)
  {
  	u32 temp;
  	MY_NVIC_PriorityGroupConfig(NVIC_Group); //设置分组
  	temp = NVIC_PreemptionPriority << (4 - NVIC_Group);
  	temp |= NVIC_SubPriority & (0x0f >> NVIC_Group);
  	temp &= 0xf;											   //取低四位
  	NVIC->ISER[NVIC_Channel / 32] |= (1 << NVIC_Channel % 32); //使能中断位(要清除的话,相反操作就OK)
  	NVIC->IP[NVIC_Channel] |= temp << 4;					   //设置响应优先级和抢断优先级
  }
  //外部中断配置函数
  //只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个
  //参数:
  //GPIOx:0~6,代表GPIOA~G
  //BITx:需要使能的位;
  //TRIM:触发模式,1,下升沿;2,上降沿;3，任意电平触发
  //该函数一次只能配置1个IO口,多个IO口,需多次调用
  //该函数会自动开启对应中断,以及屏蔽线
  void Ex_NVIC_Config(u8 GPIOx, u8 BITx, u8 TRIM)
  {
  	u8 EXTADDR;
  	u8 EXTOFFSET;
  	EXTADDR = BITx / 4; //得到中断寄存器组的编号
  	EXTOFFSET = (BITx % 4) * 4;
  	RCC->APB2ENR |= 0x01;							 //使能io复用时钟
  	AFIO->EXTICR[EXTADDR] &= ~(0x000F << EXTOFFSET); //清除原来设置！！！
  	AFIO->EXTICR[EXTADDR] |= GPIOx << EXTOFFSET;	 //EXTI.BITx映射到GPIOx.BITx
  	//自动设置
  	EXTI->IMR |= 1 << BITx; //  开启line BITx上的中断
  							//EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽这句,在硬件上是可以的,但是在软件仿真的时候无法进入中断!)
  	if (TRIM & 0x01)
  		EXTI->FTSR |= 1 << BITx; //line BITx上事件下降沿触发
  	if (TRIM & 0x02)
  		EXTI->RTSR |= 1 << BITx; //line BITx上事件上升降沿触发
  }
  //不能在这里执行所有外设复位!否则至少引起串口不工作.
  //把所有时钟寄存器复位
  void MYRCC_DeInit(void)
  {
  	RCC->APB1RSTR = 0x00000000; //复位结束
  	RCC->APB2RSTR = 0x00000000;
  
  	RCC->AHBENR = 0x00000014;  //睡眠模式闪存和SRAM时钟使能.其他关闭.
  	RCC->APB2ENR = 0x00000000; //外设时钟关闭.
  	RCC->APB1ENR = 0x00000000;
  	RCC->CR |= 0x00000001;   //使能内部高速时钟HSION
  	RCC->CFGR &= 0xF8FF0000; //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
  	RCC->CR &= 0xFEF6FFFF;   //复位HSEON,CSSON,PLLON
  	RCC->CR &= 0xFFFBFFFF;   //复位HSEBYP
  	RCC->CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
  	RCC->CIR = 0x00000000;   //关闭所有中断
  							 //配置向量表
  #ifdef VECT_TAB_RAM
  	MY_NVIC_SetVectorTable(0x20000000, 0x0);
  #else
  	MY_NVIC_SetVectorTable(0x08000000, 0x0);
  #endif
  }
  /* 
  //THUMB指令不支持汇编内联
  //采用如下方法实现执行汇编指令WFI  
  void WFI_SET(void)
  {
  	__ASM volatile("wfi");		  
  }
  //关闭所有中断
  void INTX_DISABLE(void)
  {		  
  	__ASM volatile("cpsid i");
  }
  //开启所有中断
  void INTX_ENABLE(void)
  {
  	__ASM volatile("cpsie i");		  
  }
  //设置栈顶地址
  //addr:栈顶地址
  __asm void MSR_MSP(u32 addr) 
  {
      MSR MSP, r0 			//set Main Stack value
      BX r14
  }
  */
  
  //THUMB指令不支持汇编内联
  //采用如下方法实现执行汇编指令WFI
  void WFI_SET(void)
  {
  	__asm__ __volatile__("WFI");
  }
  
  //关闭所有中断(但是不包括fault和NMI中断)
  void INTX_DISABLE(void)
  {
  
  	__asm__ __volatile__("CPSID   I");
  	__asm__ __volatile__("BX      LR");
  }
  //开启所有中断
  void INTX_ENABLE(void)
  {
  	__asm__ __volatile__("CPSIE   I");
  	__asm__ __volatile__("BX      LR");
  }
  //设置栈顶地址
  //addr:栈顶地址
  void MSR_MSP(u32 addr)
  {
  	__asm__ __volatile__("MSR MSP, r0");
  	__asm__ __volatile__("BX r14");
  }
  
  //进入待机模式
  void Sys_Standby(void)
  {
  	SCB->SCR |= 1 << 2;		 //使能SLEEPDEEP位 (SYS->CTRL)
  	RCC->APB1ENR |= 1 << 28; //使能电源时钟
  	PWR->CSR |= 1 << 8;		 //设置WKUP用于唤醒
  	PWR->CR |= 1 << 2;		 //清除Wake-up 标志
  	PWR->CR |= 1 << 1;		 //PDDS置位
  	WFI_SET();				 //执行WFI指令
  }
  //系统软复位
  void Sys_Soft_Reset(void)
  {
  	SCB->AIRCR = 0X05FA0000 | (u32)0x04;
  }
  //JTAG模式设置,用于设置JTAG的模式
  //mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;
  //#define JTAG_SWD_DISABLE   0X02
  //#define SWD_ENABLE         0X01
  //#define JTAG_SWD_ENABLE    0X00
  void JTAG_Set(u8 mode)
  {
  	u32 temp;
  	temp = mode;
  	temp <<= 25;
  	RCC->APB2ENR |= 1 << 0;   //开启辅助时钟
  	AFIO->MAPR &= 0XF8FFFFFF; //清除MAPR的[26:24]
  	AFIO->MAPR |= temp;		  //设置jtag模式
  }
  //系统时钟初始化函数
  //pll:选择的倍频数，从2开始，最大值为16
  void Stm32_Clock_Init(u8 PLL)
  {
  	unsigned char temp = 0;
  	MYRCC_DeInit();		   //复位并配置向量表
  	RCC->CR |= 0x00010000; //外部高速时钟使能HSEON
  	while (!(RCC->CR >> 17))
  		;					//等待外部时钟就绪
  	RCC->CFGR = 0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
  	PLL -= 2;				//抵消2个单位（因为是从2开始的，设置0就是2）
  	RCC->CFGR |= PLL << 18; //设置PLL值 2~16
  	RCC->CFGR |= 1 << 16;   //PLLSRC ON
  	FLASH->ACR |= 0x32;		//FLASH 2个延时周期
  	RCC->CR |= 0x01000000;  //PLLON
  	while (!(RCC->CR >> 25))
  		;					 //等待PLL锁定
  	RCC->CFGR |= 0x00000002; //PLL作为系统时钟
  	while (temp != 0x02)	 //等待PLL作为系统时钟设置成功
  	{
  		temp = RCC->CFGR >> 2;
  		temp &= 0x03;
  	}
  }
  