STM32F103单片机编程入门
一款单片机入门,至少四样:时钟、端口、定时、串口、中断。
系统时钟
RCC
系统内部有8M_RC晶振和32678Hz_RC晶振有大约2%的温飘。当外部有8M
晶振时,自动选择外部晶振,失效时自动切换成内部。程序自动倍频成72M。
如果用于通信最好加个外部晶振。判断是否使用外部晶振的方法:短接外部晶
振引脚观察工作情况。
分为两个桥,对应不同的外设,每个外设又可以单独设定时钟。
初步学习,先不用单独设定,均选用系统时钟72M。可根据情况做一步分频。
用到某外设时,配置RCC(打开外设时钟),一般只有一句指令。一般临时查找。呵呵,我也没找到好办法。
GPIO:RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE); USART:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
Timer2:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
端口GPIO
端口配置思路:
1,先定义一个结构体配置成员参数值,
类型是GPIO_InitTypeDef,下划线是结构体名;结构体名是GPIO_InitStructure:名称可以自定义。在后面利用参数初始化函数时要一致。
2,打开相对应的端口时钟RCC。
3,声明要配置的管脚,可以用“|”复选
4,配置模式,4种输入,4种输出
5,配置管脚频率,一般都是50Mhz
6,最后调用函数GPIO_Init(GPIOA, &GPIO_InitStructure);第2个参数是,结构体地址指针。
Eg:
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
一、串口
USART
串口配置思路:
1,定义结构体,类型是USART_InitTypeDef;
2,打开串口时钟,可以选择和端口GPIO一起
3,设置波特率,—————省去了复杂的烦人的计算
4,设置字长。8位?9位?
5,设置停止位。1位?2位?
6,设置校验位,奇偶?无?
7,设置硬件流(调制解调器用)————用不到设None就行
8,串口工作模式:收?发?都有?
9,调用函数USART_Init(USART1, &USART_InitStructure); 配置串口
10,开启串口中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);或USART_IT_TXE, ENABLE);收发中断的使能。
11,中断响应函数void USART1_IRQHandler(void)
12,取出缓存数据data=USART_ReceiveData(USART1);读操作自动清零串口接
受标志位。
13,发送数据USART_SendData(USART2,FromScreen[Ua1])和
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);等待发送
完成(寄存器非空)。
Eg:
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |
RCC_APB2Periph_USART1,ENABLE);
USART_https://www.doczj.com/doc/7b11585614.html,ART_BaudRate = 9600;
USART_https://www.doczj.com/doc/7b11585614.html,ART_WordLength = USART_WordLength_8b;
USART_https://www.doczj.com/doc/7b11585614.html,ART_StopBits = USART_StopBits_1;
USART_https://www.doczj.com/doc/7b11585614.html,ART_Parity = USART_Parity_No;
USART_https://www.doczj.com/doc/7b11585614.html,ART_HardwareFlowControl
=USART_HardwareFlowControl_None; USART_https://www.doczj.com/doc/7b11585614.html,ART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
void USART1_IRQHandler(void) //串口1中断函数,接收终中断
{
Data=USART_ReceiveData(USART1); //接受道到的数据,存入变量Data;
Receive_Flag1 = 1;//自定义标识。
}
if(Receive_Flag1 == 1) //用串口2发送串口1收到的数据。
{
USART_SendData(USART2,FromScreen[Ua1]);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)//等待发送完成Receive_Flag1 = 0;
}
二、中断概述及思路
概述:
STM32中断较复杂;有几个优先级,哪个高哪个低都可以自由配置——用NVIC管理并配置:因为中断源多,所以用分组不同的优先级来不同管理,组与组之间没有关系,互不干扰,同一组之间分:抢占式(主)和响应式(从)。
抢占式优先级可以嵌套,同在抢占式则1#抢(嵌套)2#,有几个抢占级别就有几级优先级。抢占式级别相同的时候,如果同一时间同时到来,按从优先级排序。但是不嵌套。
编程思路:
1,定义结构体,类型是NVIC_InitTypeDef
2,确定一下选用哪一组,也就是想好用几级优先级。
3,设置中断源
4,设置主优先级号,也就是级别
5,设置从优先级号,也就是主优先级相同时,排序不嵌套。
6,使能,函数NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
7,NVIC配置函数 NVIC_Init(&NVIC_InitStructure);
Eg:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //选用第1组,2个优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //中断源,串口1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //主优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
三、定时器概述以及编
程思路(不含RTC
——实时时钟)
概述:STM32F103C8T6,定时器有:滴答时钟1个SysTick timer、高级定时器1个Time1、通用定时器3个Time2~Time4、看门狗定时器2个WatchDog;
通用定时器需要了解在RCC哪里(ABP1还是ABP2?),时钟频率。
1,定义结构体,
类型是TIM_TimeBaseInitTypeDef;名为TIM_TimeBaseStructure;
2,启动RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); 3,最好先参数缺省一下(防止之前已经设置了参数)TIM_DeInit(TIM2);
4,定时周期(自动重载,^_^)TIM_TimeBaseStructure.TIM_Period=1000;
//1000个Cnt计数值(<65535)。Cnt时基呢?看下面。
5,设定定时时基即采样周期(二级设定避免计数值过大)
TIM_TimeBaseStructure.TIM_Prescaler= (72 - 1);//主频72M。时基就是
72/72 000 000=1us,Prescaler值也是<65535。所以上面定时1ms。如果要分
频就不是72M了,看下面。
6,定时器基础脉冲是否分频?如果分频上面就不是1us了;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//不分频72M 7,计数模式,向上计数还是向下?
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计
数
8,调用函数初始化参数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
9,清除(复位)中断标志,防止立即产生一个中断。(也就是溢出中断)TIM_ClearFlag(TIM2, TIM_FLAG_Update);
10,使能定时器2的中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
11,启动定时器2 TIM_Cmd(TIM2, ENABLE);//*开启定时器
12,定时中断函数void TIM2_IRQHandler(void);不用再记忆中断号了,哈哈
13,进入定时器中断的程序的原因不唯一,比如:更新事件的产生,捕获事件的产生等,故在进入中断的一开始确认利用这句话确定是不是确定发生了更新事件。
所以加一句判断if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET );
//更新产生的中断即重载中断。
14,必须程序复位标志位
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);或者用另一种方法
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
Eg
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period=1000; //1000个时基
TIM_TimeBaseStructure.TIM_Prescaler= (36000- 1); //时基0.5ms
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //不分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; /* 向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM2, ENABLE);
void TIM2_IRQHandler(void) //TIM2的中断服务程序。
{
int display;
display=GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13);//读出端口的状态。
if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET )
{
if( !display ) GPIO_SetBits(GPIOC,GPIO_Pin_13);
else GPIO_ResetBits(GPIOC,GPIO_Pin_13);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//这一句,和上一句,效
果是一样的,所以,只用一句也行。
}
}