0 keil5库函数配置
1、建立工程文件夹,Keil中新建工程,选择型号
2、工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹。
Start文件夹中:
- 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm下,8个启动文件之一(stm32f103c8 64kflash,需要后缀是md.s,就复制这一个),程序从启动文件开始执行。
- 复制\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 下的3个文件,stm32f10x.h(描述外设寄存器名称和地址,类似regx52.h),system_stm32f10x.c, system_stm32f10x.h(配置时钟用)
- 复制\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport 下的两个文件 core_cm3.c,core_cm3.h(内核寄存器描述及内核的配置函数)
User文件夹:
- 建立文件main.c
- 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template下的 stm32f10x_conf.h(配置库函数头文件的包含关系及用来函数检查的函数定义),stm32f10x_it.c,stm32f10x_it.h(用来存放中断函数的)
Library文件夹:
- 复制 \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src 下的所有文件 misc.c(内核的库函数),其他的是外设的库函数
- 复制和src同级文件夹inc中的所有头文件
3、工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里(用keil5中的工程管理Manage Project Items)
4、工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹Start、Library、Use以及根据项目需要建立的文件夹
5、工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
6、工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run
1 GPIO模块
一、库函数点亮LED
led.h
#ifndef __LED_H__
#define __LED_H__
#define LED_GPIO_Port GPIOB
#define LED_GPIO_Pin GPIO_Pin_8
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB
void Led_Init(void);
void LED_GPIO_High(void);
void LED_GPIO_Low(void);
void LED_GPIO_Cpl(void);
#endif
led.c
#include "stm32f10x.h"
#include "led.h"
void Led_Init(void)
{
RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=LED_GPIO_Pin;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
}
void LED_GPIO_High(void)
{
GPIO_SetBits(LED_GPIO_Port, LED_GPIO_Pin);
}
void LED_GPIO_Low(void)
{
GPIO_ResetBits(LED_GPIO_Port, LED_GPIO_Pin);
}
void LED_GPIO_Cpl(void)
{
GPIO_WriteBit(LED_GPIO_Port, LED_GPIO_Pin, \
(BitAction)(GPIO_ReadOutputDataBit(LED_GPIO_Port, LED_GPIO_Pin)? 0:1));
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "Delay.h"
int main(void)
{
Led_Init();
while(1)
{
LED_GPIO_High();
Delay_ms(500);
LED_GPIO_Low();
Delay_ms(500);
LED_GPIO_Cpl();
Delay_ms(500);
LED_GPIO_Cpl();
Delay_ms(500);
}
}
2 SYSTICK滴答定时器
systick 实现延时程序1(启停定时器影响CLKSOURCE位)
void Delay_us(uint32_t xus) //最大72*xus不能超过2的24次方
{
SysTick->LOAD = 72 * xus; //设置定时器重装值(72Mhz,计数72个为1us)
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //置位CLKSOURCE,ENABLE,时钟源为72Mhz。如果复位,除以8为9Mhz。
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器,
}//SysTick等同于((SysTick_Type*)(0xE000E010)) SysTick_Type是结构体
systick 实现延时程序2
void delay_init()
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟,HCLK/8为9Mhz。
fac_us = SystemCoreClock / 8000000; //数值为9
fac_ms = (u16)fac_us*1000; //数值为9000
}
void Delay_us(u32 xus) //void Delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = fac_us * xus; //设置定时器重装值us,LOAD值不能超过2的24次方
//SysTick->LOAD = (u32)fac_ms * nms; //设置定时器重装值ms,最大1864ms
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //启动定时器
do
{
temp = SysTick->CTRL;
}while((temp & 0x01) && !(temp&(1<<16))); //如果定时器已经启动并且COUNTFLAG为0,等待
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 停止计数器
SysTick->VAL = 0x00; //清空当前计数值
}
systick 设置计数值,中断处理
SysTick_Config(uint32_t ticks) //时钟源为内部时钟72Mhz,参数最大不能超过0xFFFFFF,大概233ms,设置中断优先级,
void SysTick_Handler(void)
{
LED_GPIO_Cpl();
}
3 外部中断编程
外部中断EXTI
void exti_init(void) //PB9 外部中断
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); //EXTI、NVIC时钟不用手动开启
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9); //afio选择外部中断引脚
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式,别的选项是事件模式
EXTI_InitStruct.EXTI_Line=EXTI_Line9;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发,别的选项上升沿、上升下降沿
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=EXTI9_5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line9)==SET)
{
//中断处理代码
EXTI_ClearITPendingBit(EXTI_Line9);
}
}
4 定时器编程
void time3_init(u16 arr, u16 psc) //arr为9999,psc为7199,定时周期为10000*7200/(72*1000000)=1s
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能time3时钟
TIM_InternalClockConfig(TIM3); //配置time3 选择时钟为内部时钟
//选择ETR引脚通过外部时钟模式2输入的时钟(对time2为PA0,time3为PD2)
//TIM_ETRClockMode2Config(TIM3, TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
//参数:预分频,极性,滤波器(采样频率和采样次数),当然需要对GPIO引脚配置,浮空或者上拉输入
/*
TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx其它定时器的时钟
TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter); //选择TIx捕获通道的时钟
TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter); //选择ETR通过外部时钟模式1输入的时钟
TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);//用来单独配置ETR引脚的预分频器、极性、滤波器这些参数的
*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; //对CNT计数器,SetCounter和GetCounter读写
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //死区及数字滤波采样时钟分频比
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStruct.TIM_Period=arr; //重装载值
TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //预分频系数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); //配置时基单元
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //解决刚运行就进入中断的问题
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //定时器中断中使能更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组,可以在main函数中出现一次
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn; //中断源为time3
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //等待优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //抢占优先级
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM3, ENABLE); //使能定时器
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update)==SET)
{
LED_GPIO_Cpl(); //反转led口
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
5 串口编程模块
状态寄存器重要位:
TXE:发送数据寄存器空(Transmit data register empty)复位为空1
TC:发送完成(Transmission complete)复位为完成1
RXNE:读数据寄存器非空(Read data register not empty)复位为空0
void my_usart1_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //串口1:PA9 TX 发送引脚
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //PA10 RX 接受引脚
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate=9600; //波特率
USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; //发送接收全双工
USART_InitStruct.USART_Parity=USART_Parity_No; //奇偶校验位无
USART_InitStruct.USART_StopBits=USART_StopBits_1; //停止位1
USART_InitStruct.USART_WordLength=USART_WordLength_8b; //数据位数
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件数据流控制
USART_Init(USART1, &USART_InitStruct);
//开启中断并且配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
void usart_send_string(USART_TypeDef* USARTx, char *str)
{
while(*str!='\0')
{
USART_SendData(USARTx, (u16)(*str++));
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==0);
}
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)==0);
}
void USART_SendByte(USART_TypeDef* USARTx, uint8_t Data)
{
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_DATA(Data));
USARTx->DR = (Data & (uint16_t)0x01FF);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
}
void USART1_IRQHandler(void)
{
u16 temp;
if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET)
{
temp = USART_ReceiveData(USART1);
USART_SendByte(USART1, temp);
// USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
使用printf语句,方便把信息通过串口输出到电脑显示,下面是响应的设置
#pragma import(__use_no_semihosting) //在串口模块输入下面代码
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x=x;
}
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (u8)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
return ch;
}
//重定向c库函数scanf到串口,重定向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)==RESET);
return (int)USART_ReceiveData(USART1);
}
/* // main.c程序中使用printf,当然 需要include "stdio.h"
u32 temp=12345;
float f=12.3478;
my_usart1_init(); //使用printf可以不开启串口中断
printf("temp=%d\r\n", temp);
printf("temp=%f\r\n", f);
*/