一、GPIO输入输出
1、GPIO输出
Init初始化函数
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure; //实例化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);//使能D端口的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//配置端口引脚13
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//配置输入输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置速度
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化}
GPIO输出设置
GPIO_ResetBits(GPIOD,GPIO_Pin_13);//复位低电平
GPIO_SetBits( GPIOD,GPIO_Pin_13);//置位高电平
2、GPIO输入
Init初始化函数
void key_init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启时钟
//配置端口模式
GPIO_InitTypeDef GPIO_InitStructB; //结构体变量名GPIO_InitStructB
GPIO_InitStructB.GPIO_Mode = GPIO_Mode_IPU; //需要上拉输入,按键未按时默认高电平
GPIO_InitStructB.GPIO_Pin = GPIO_Pin_1 |GPIO_Pin_11;//具体引脚
GPIO_InitStructB.GPIO_Speed = GPIO_Speed_50MHz;//在输入模式下,这个参数无影响
GPIO_Init(GPIOB,&GPIO_InitStructB);
}
GPIO输入设置
uint8_t key_getnum(void)
{
uint8_t keynum = 0; //按键键码,没有按下,就返回0,局部变量
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0){ //读取GPIO端口
Delay_ms(20); //按键按下消抖20ms
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0); // 松手后动作,直到松手
Delay_ms(20); //按键松手消抖
keynum = 1; //键码为1.传递出去
}
return keynum;
}
二、中断
1、EXTI外部中断
外部中断初始化Init
void countsensor_init(void)
{
//第一步,配置GPIO PinB14口RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启RCC时钟
GPIO_InitTypeDef GPIO_initstruct; //结构体名字GPIO_initstruct
GPIO_initstruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_initstruct.GPIO_Pin = GPIO_Pin_14;
GPIO_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_initstruct); //传地址
//第二步,按键中断配置
//AFIO的库函数是和GPIO在一个文件里,可以查看Library文件中的gpio.h查看函数RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//代表连接PB14号口的第14个中断线路
EXTI_InitTypeDef EXTI_InitStructure;//结构体类型名EXTI_InitTypeDef
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//因为上面GPIO_Mode_IPU设置为高电平,所以触发中断是下降
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//第三步,配置NVIC系统中断配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组方式
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//因为我们这个程序只有一个,所以中断优先级的配置也是非常随意的
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
(1)所有GPIO口都能触发中断,但相同的Pin不能同时触发中断(比如PA0和PB0不能同时使用,智能选一个作为中断引脚;所以如果有多个中断引脚要选择不同的pin引脚,比如PA0和PA1、PB3就可以)。
(2)GPIO_initstruct.GPIO_Mode模式为浮空输入|上拉输入|下拉输入;不知该写什么模式,可以看参考手册中的外设GPIO配置。
(3) NVIC_PriorityGroupConfig()分组方式,整个芯片只能用一种。如放在模块中进行分组,要确保每个模块分组都选的是同一个;或者将这个代码放在主函数的最开始
(4) NVIC_InitStructure.NVIC_IRQChannel()通道需要去stm32f10x.h里面的typedef enum IRQn中去找
外部中断执行函数
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line14) == SET)
{
//中断代码//·········
EXTI_ClearITPendingBit(EXTI_Line14);
}
中断函数都是无参无返回值的,名字固定在startup文件向量表中,一般都是先进行一个中断标志位的判断,确保是我们想要的中断源触发的函数,因为这个函数EXTI10到EXTI15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的,每次中断函数结束后,都应该清除一下中断标志位
2、TIM定时器中断
TIM定时器Init初始化函数
void Timer_Init(void) //定时中断初始化代码
{
//配置时基单元
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启APB1时钟
TIM_InternalClockConfig(TIM2);//选择时基单元的时钟,选择内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //指定时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启更新中断到NVIC的通道
//NVIC中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC优先级分组
NVIC_InitTypeDef NVIC_InitTyStructure;
NVIC_InitTyStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitTyStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitTyStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitTyStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitTyStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);//当产生更新时,就会触发中断
}
/*在TIM_TimeBaseInit函数的最后,会立刻生成一个更新事件,来重新装载预分频器和重复计数器的值
预分频器有缓冲寄存器,我们写入的PSC和ARR只有在更新事件时才会起作用
为了让写入的值立刻起作用,故在函数的最后手动生成了一个更新事件
但是更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,手动生成一个更新事件,就相当于在初始化时立刻进入更新函数执行一次
在开启中断之前手动清除一次更新中断标志位,就可以避免刚初始化完成就进入中断函数的问题*/
/*定时1s也就是定时频率为1Hz,定时频率=72M/ (PSC + 1) / (ARR + 1) = 1s =1Hz,
那就可以PSC给7200,ARR给10000(1MHz等于10^6Hz),然后两个参数再减1
在这里预分频是对72M进行7200分频,得到的就是10k的计数频率,
在10k的频率下,计10000个数,就是1s的时间*/
TIM定时器执行函数
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
//执行相应的用户代码
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位
}
}
3、 TIM外部中断
TIM外部中断Init初始化函数
void Timer_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//外部模式2需要用到gpio,进行GPIOA的时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//通过ETR引脚的外部时钟模式2配置
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //指定时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启更新中断到NVIC的通道
//NVIC中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC优先级分组
NVIC_InitTypeDef NVIC_InitTyStructure;
NVIC_InitTyStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitTyStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitTyStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitTyStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitTyStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);//当产生更新时,就会触发中断
}
TIM外部中断Init初执行函数
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
//执行相应的用户代码
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位
}
}
三、通信接口协议
1、USART串口协议
串口初始化函数
tx引脚(A9)是USART外设控制输出引脚,选择复用推挽输出。rx引脚(A10)是USART外设控制输入引脚,选择输入模式(浮空输入或者上拉输入)
void usart_init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStruture;
USART_InitStruture.USART_BaudRate = 9600;
USART_InitStruture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruture.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruture.USART_Parity = USART_Parity_No;
USART_InitStruture.USART_StopBits = USART_StopBits_1;
USART_InitStruture.USART_WordLength = USART_WordLength_8b;
//中断设置USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitTyStructure;
NVIC_InitTyStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitTyStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitTyStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitTyStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitTyStructure);
USART_Cmd(USART1, ENABLE);
}
随后可以在中断函数中进行串口接收
void USART1_IRQHandler(void){}
串口发送
void Serial_SendByte(uint8_t Byte){
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}//发送字符串
void Serial_SendString(char *String){
uint16_t i;
for(i = 0;String[0] != '\0'; i++){
Serial_SendByte(String[i]);
}
}//发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length){
uint16_t i;
for(i = 0;i < Length; i++){
Serial_SendByte(Array[i]);
}
}
使用printf()函数输出到串口助手,在Keil工程选项中,勾选Use MicroLIB,然后添加头文件#include <stdio.h>,编写代码:
int fputc(int ch, FILE *f){
Serial_SendByte(ch);
return ch;
}
串口接收
单字节接收,接收到的字节存在Usart_Receive变量中
u8 Usart_Receive;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Usart_Receive = USART_ReceiveData(USART1);USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
Hex数据包接收(数组接收),规定好帧头帧尾,发送字节长度,比如一共发11字节,然后依次接收存在数组 Receive_Data.buffer(需要提前定义)。其中FRAME_HEADER为帧头字节宏定义,FRAME_TAIL为帧尾宏定义,还包含校验位使用Check_Sum()自定义函数
也可以使用文本数据包进行接收。
int USART1_IRQHandler(void)
{
static u8 Count=0;
u8 Usart_Receive;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Usart_Receive = USART_ReceiveData(USART3);
Receive_Data.buffer[Count]=Usart_Receive;
if(Usart_Receive == FRAME_HEADER||Count>0)
Count++;
else
Count=0;
if (Count == 11)
{
Count=0;
if(Receive_Data.buffer[10] == FRAME_TAIL)
{
if(Receive_Data.buffer[9] ==Check_Sum(9,0))
{
//接收处理代码············
}
}
}
}
return 0;
}
校验函数
u8 Check_Sum(unsigned char Count_Number,unsigned char Mode)
{
unsigned char check_sum=0,k;
//对发送的数据进行校验
if(Mode==1)
for(k=0;k<Count_Number;k++)
{
check_sum=check_sum^Send_Data.buffer[k];
}
//对接收的数据进行校验
if(Mode==0)
for(k=0;k<Count_Number;k++)
{
check_sum=check_sum^Receive_Data.buffer[k];
}
return check_sum;
}