#include "st_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 Sys_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 Sys_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 Sys_NVIC_Init(u8 NVIC_PreemptionPriority, u8 NVIC_SubPriority, u8 NVIC_Channel, u8 NVIC_Group) { u32 temp; Sys_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<FTSR |= 1 << BITx; //line BITx上事件下降沿触发 if (TRIM & 0x02) EXTI->RTSR |= 1 << BITx; //line BITx上事件上升降沿触发 } #define EVCR_PORTPINCONFIG_MASK ((uint16_t)0xFF80) #define LSB_MASK ((uint16_t)0xFFFF) #define DBGAFR_POSITION_MASK ((uint32_t)0x000F0000) #define DBGAFR_SWJCFG_MASK ((uint32_t)0xF0FFFFFF) #define DBGAFR_LOCATION_MASK ((uint32_t)0x00200000) #define DBGAFR_NUMBITS_MASK ((uint32_t)0x00100000) /** * 管脚复用设置 * * @author lxz (121319 14:46:01) * * @param GPIOx * @param BITx * @param AFx */ void GPIO_Remap_Set(uint32_t GPIO_Remap, u8 newstate) { uint32_t tmp = 0x00, tmp1 = 0x00, tmpreg = 0x00, tmpmask = 0x00; if ((GPIO_Remap & 0x80000000) == 0x80000000) { tmpreg = AFIO->MAPR2; } else { tmpreg = AFIO->MAPR; } tmpmask = (GPIO_Remap & DBGAFR_POSITION_MASK) >> 0x10; tmp = GPIO_Remap & LSB_MASK; if ((GPIO_Remap & (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) == (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) { tmpreg &= DBGAFR_SWJCFG_MASK; AFIO->MAPR &= DBGAFR_SWJCFG_MASK; } else if ((GPIO_Remap & DBGAFR_NUMBITS_MASK) == DBGAFR_NUMBITS_MASK) { tmp1 = ((uint32_t)0x03) << tmpmask; tmpreg &= ~tmp1; tmpreg |= ~DBGAFR_SWJCFG_MASK; } else { tmpreg &= ~(tmp << ((GPIO_Remap >> 0x15) * 0x10)); tmpreg |= ~DBGAFR_SWJCFG_MASK; } if (newstate != 0) { tmpreg |= (tmp << ((GPIO_Remap >> 0x15) * 0x10)); } if ((GPIO_Remap & 0x80000000) == 0x80000000) { AFIO->MAPR2 = tmpreg; } else { AFIO->MAPR = tmpreg; } } /** * 管脚模式设置 * * @author lxz (121319 14:46:04) * * @param GPIOx * @param BITx * @param MODE * @param OTYPE * @param OSPEED * @param PUPD */ void GPIO_Set(GPIO_TypeDef *GPIOx, u32 BITx, u32 MODE, u32 OSPEED, u32 PUPD) { u32 pinpos = 0, pos = 0, curpin = 0; int currentmode = 0; for (pinpos = 0; pinpos < 16; pinpos++) { pos = 1 << pinpos; //一个个位检查 curpin = BITx & pos; //检查引脚是否要设置 if (curpin == pos) { //需要设置 //获取设置 currentmode = 0; if (MODE != GPIO_MODE_IN_FLOATING && MODE != GPIO_MODE_AIN && MODE != GPIO_MODE_IPD && MODE != GPIO_MODE_IPU) { //如果是输出模式 currentmode |= OSPEED; //设置输出速度 } if (PUPD == GPIO_PUPD_PU) { //设置上拉 GPIOx->BSRR = 1 << pinpos; } else if (PUPD == GPIO_PUPD_PD) { //设置下拉 GPIOx->BRR = 1 << pinpos; } currentmode |= MODE & 0x0f; //将设置保存到寄存器 if (pinpos < 8) { GPIOx->CRL &= ~(0x0f << (pinpos * 4)); GPIOx->CRL |= currentmode << (pinpos * 4); } else { GPIOx->CRH &= ~(0x0f << ((pinpos - 8) * 4)); GPIOx->CRH |= currentmode << ((pinpos - 8) * 4); } } } } //不能在这里执行所有外设复位!否则至少引起串口不工作. //把所有时钟寄存器复位 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 //Sys_NVIC_SetVectorTable(0x20000000, 0x0); #else //Sys_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:栈顶地址 void MSR_MSP(u32 addr) { __ASM volatile("MSR MSP, r0"); //set Main Stack value __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->APB1ENR |=1<<28; //PMU->CTL |= 3<<14; 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锁定 /* enable the high-drive to extend the clock frequency to 120 MHz */ //PMU->CTL |= 1<<16; //while(0U == (PMU->CS & (1<<16))){ //} /* select the high-drive mode */ // PMU->CTL |= 1<<17; //while(0U == (PMU->CS & (1<<17))){ //} RCC->CFGR |= 0x00000002; //PLL作为系统时钟 while (temp != 0x02) { //等待PLL作为系统时钟设置成功 temp = RCC->CFGR >> 2; temp &= 0x03; } }