#include "sys.h"
#include "usart.h"
#include "delay.h"
//ALIENTEK精英STM32F103开发板 实验0
//新建工程 实验
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司
int main(void)
{
u8 t=0; //见注释1
Stm32_Clock_Init(9); //1.系统时钟设置
delay_init(72); //2.延时初始化
uart_init(72,115200); //3.串口初始化为115200
while(1)
{
printf("t:%d\r\n",t);
delay_ms(500);
t++;
}
}
程序详解:
1.系统时钟设置
//系统时钟初始化函数
//pll:选择的倍频数,从2开始,最大值为16
void Stm32_Clock_Init(u8 PLL)
{
unsigned char temp=0;
MYRCC_DeInit(); //1.1复位并配置向量表
RCC->CR|=0x00010000; //外部高速时钟使能HSEON--HSE振荡器开启
while(!(RCC->CR>>17));//等待外部时钟就绪--外部高速时钟就绪标志 1:外部4-16MH振//荡器就绪,此语句应改为:while(!(RCC->CR>>17)&0X01);
RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;--低速APB预分频 位10:8 //100: HCLK 2分频
PLL-=2; //抵消2个单位(因为是从2开始的,设置0就是2)
RCC->CFGR|=PLL<<18; //设置PLL值 2~16--位21:18 PLL倍频系数
RCC->CFGR|=1<<16; //PLLSRC ON -- PLL输入时钟源 1: HSE时钟作为PLL输入时钟。
FLASH->ACR|=0x32; //1.2闪存访问控制寄存器FLASH 2个延时周期--位2~0 LATENCY:时延 这些位表示//SYSCLK(系统时钟)周期与闪存访问时间的比例
//000:零等待状态,当 0 < SYSCLK ≤ 24MHz
//001:一个等待状态,当 24MHz < SYSCLK ≤ 48MHz
//010:两个等待状态,当 48MHz < SYSCLK ≤ 72MHz
//--位3 HLFCYA:闪存半周期访问使能 0:禁止半周期访问;
//--位4 PRFTBE:预取缓冲区使能 1:启用预取缓冲区。
//--位5 PRFTBS:预取缓冲区状态 1:预取缓冲区开启。
RCC->CR|=0x01000000; //PLLON--位24 PLLON: PLL使能 1: PLL使能
while(!(RCC->CR>>25));//等待PLL锁定--位25 PLLRDY: PLL时钟就绪标志 1: PLL锁定。
RCC->CFGR|=0x00000002;//PLL作为系统时钟--位1:0 SW[1:0]:系统时钟切换
while(temp!=0x02) //等待PLL作为系统时钟设置成功
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
}
-
- 复位并配置向量表
//不能在这里执行所有外设复位!否则至少引起串口不工作.
//把所有时钟寄存器复位
void MYRCC_DeInit(void)
{
RCC->APB1RSTR = 0x00000000;//复位结束--APB1外设复位寄存器 0为无作用
RCC->APB2RSTR = 0x00000000;//--APB2 外设复位寄存器 0为无作用
RCC->AHBENR = 0x00000014; //睡眠模式闪存和SRAM时钟使能.其他关闭--AHB外//设时钟使能寄存器,1:睡眠模式时SRAM时钟开启,1:睡眠模式时闪存接口电路时//钟开启
RCC->APB2ENR = 0x00000000; //外设时钟关闭.--APB2 外设时钟使能寄存器
RCC->APB1ENR = 0x00000000; //--APB1 外设时钟使能寄存器
RCC->CR |= 0x00000001; //使能内部高速时钟HSION--时钟控制寄存器,1:内部//8MHz振荡器开启。
RCC->CFGR&=0xF8FF0000;//复位
//时钟配置寄存器SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
//--00: HSI作为系统时钟;AHB预分频 0xxx: //SYSCLK不分频;低速APB预分频 0xx: HCLK不分频;高速APB预分频 0xx: HCLK不分//频;ADC预分频 00: PCLK2 2分频后作为ADC时钟;微控制器时钟输出 100:系统时钟//(SYSCLK)输出
RCC->CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON--外部高速时钟使能 0: HSE
//振荡器关闭;时钟安全系统使能 0:时钟监测器关闭;PLL使能 0: PLL关闭;
RCC->CR &= 0xFFFBFFFF; //复位HSEBYP--外部高速时钟旁路 0:外部4-16MHz振荡//器没有旁路;
RCC->CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
//--PLL输入时钟源 0: HSI振荡器时钟经2分频后作为PLL输入时钟; HSE分频器作为PLL输入 0: HSE不分频
//--PLL倍频系数 0000: PLL 2倍频输出;USB预分频 0: //PLL时钟1.5倍分频作为USB时钟
RCC->CIR = 0x00000000; //关闭所有中断--时钟中断寄存器
//配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(0x20000000, 0x0);//注释1.1.1
#else
MY_NVIC_SetVectorTable(0x08000000,0x0);
#endif
}
1.1.1//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{
SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器
//用于标识向量表是在CODE区还是在RAM区
}
SCB 为 MDK 定义的一个寄存器组,里面包含了很多与内核相关的控制器, 该结构体在core_m3.h 里面
1.2 FLASH->ACR|=0x32; //闪存访问控制寄存器
2.延时初始化
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
SysTick->CTRL&=~(1<<2); //SYSTICK使用外部时钟源
fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload=SYSCLK/8; //每秒钟的计数次数 单位为K
reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最值:16777216,
//在72M下,约合1.86s左右
fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL|=1<<1; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次
SysTick->CTRL|=1<<0; //开启SYSTICK
#else
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
#endif
}
3.串口初始化
//初始化IO 串口1
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;//--尾数
u16 fraction;//--分数
temp=(float)(pclk2*1000000)/(bound*16);//得到//USARTDIV--Baud=fck/(16*USARTDIV),USARTDIV为16进制数,分尾数和分数
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;//
mantissa+=fraction; //--填入的16进制数
RCC->APB2ENR|=1<<2; //使能PORTA口时钟--位2 IOPAEN: IO端口A时钟使能
//(I/O port A clock enable)
RCC->APB2ENR|=1<<14; //使能串口时钟 --位14 USART1EN: USART1时钟使能 (USART1 clock enable)
GPIOA->CRH&=0XFFFFF00F;//IO状态设置--设置TX RX 口的推挽输出、浮空输入模式
GPIOA->CRH|=0X000008B0;//IO状态设置
RCC->APB2RSTR|=1<<14; //复位串口1--位14 USART1RST: USART1复位 1:复位USART1。
RCC->APB2RSTR&=~(1<<14);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置--位3:0 DIV_Fraction[3:0]: USARTDIV的小数
//部分 位15:4 DIV_Mantissa[11:0]: USARTDIV的整数部分
USART1->CR1|=0X200C; //1位停止,无校验位.
#if EN_USART1_RX //如果使能了接收
//使能接收中断
USART1->CR1|=1<<5; //接收缓冲区非空中断使能--位5 RXNEIE:接收缓冲区非空中
//断使能 1:当USART_SR中的ORE或者RXNE为’1’时,产生USART中断。
MY_NVIC_Init(3,3,USART1_IRQn,2);//3.1 组2,最低优先级
#endif
}
3.1 MY_NVIC_Init(3,3,USART1_IRQn,2);
//设置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);//3.1.1设置分组
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);//--//(4-NVIC_Group) 组0 左移4位 0
//位抢占 ,组1 左移3位 1位抢占,组2 左移2位 2位抢占,组3 左移1位 3位抢//占,组4 左移0位,4位抢占
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);//--(0x0f>>NVIC_Group)限定位 组0 1111 //4位响应,组1 0111 3位响应 , 组2 0011 2位响应, 组3 0001 1位响应, 组//4 0000,0位响应
temp&=0xf; //取低四位
NVIC->ISER[NVIC_Channel/32]|=(1<<NVIC_Channel%32);//使能中断位(要清除的话,相反操//作就OK)--ISER[0]的 bit0~bit31 分别对应中断 0~31
NVIC->IP[NVIC_Channel]|=temp<<4; //设置响应优先级和抢断优先级--设置响应优//先级和抢断优先级
}
3.1.1 MY_NVIC_PriorityGroupConfig(NVIC_Group);
//设置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; //读取先前的设置--寄存器的[10:8]3 位就是 PRIGROUP 的定义位,//它的值规定了系统中
//有多少个抢先级中断和子优先级中断,此值与分组值为取反关系
temp&=0X0000F8FF; //清空先前分组
temp|=0X05FA0000; //写入钥匙
temp|=temp1;
SCB->AIRCR=temp; //设置分组
}
注释1.
typedef struct
{
__O union
{
__O uint8_t u8; /*!< Offset: ITM Stimulus Port 8-bit */
__O uint16_t u16; /*!< Offset: ITM Stimulus Port 16-bit*/
__O uint32_t u32; /*!< Offset: ITM Stimulus Port 32-bit */
} PORT [32]; /*!< Offset: 0x00 ITM Stimulus Port Registers*/
uint32_t RESERVED0[864];
__IO uint32_t TER; /*!< Offset: ITM Trace Enable Register */
// typedef unsigned int uint32_t;
uint32_t RESERVED1[15];
__IO uint32_t TPR; /*!< Offset: ITM Trace Privilege Register*/
Identification Register #2 */
…..(此处省略部分内容)
__I uint32_t CID3; /*!< Offset: ITM Component Identification Register #3 */
} ITM_Type;
共用体(参考"共用体"百科词条)是一种特殊形式的变量,使用关键字union来定义
共用体(有些人也叫"联合")声明和共用体变量定义与结构体十分相似。其形式为:
union 共用体名{
数据类型 成员名;
数据类型 成员名;
...
} 变量名;
共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。
结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。
而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使用更为精细灵活,也节省了内存空间。
示例: typedef struct Person {
char name[20];
char sex;
float height;
int age;
}Per; 这种写法在声明结构体变量的时候就可以用 Per xiaoming;
如果没有typedef 就必须用 struct Person,这里的Per实际上就是 struct Person的别名
/* IO definitions define access restrictions to peripheral registers*/
#ifdef __cplusplus
#define __I volatile /*!< defines 'read only' permissions */
#else
#define __I volatile const /*!< defines 'read only' permissions */
#endif
#define __O volatile /*!< defines 'write only' permissions */
#define __IO volatile /*!< defines 'read / write' permissions */