代码修改与调用模块:
1.GPIO输入通用—LED
/*********************************************************************************************************
* 内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:ConfigLEDGPIO
* 函数功能:配置LED的GPIO
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static void ConfigLEDGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
//使能RCC相关时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOC的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //设置引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置I/O输出速度
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置模式
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化LED1的GPIO
GPIO_WriteBit(GPIOC, GPIO_Pin_4, Bit_SET); //将LED1默认状态设置为点亮
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //设置引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置I/O输出速度
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置模式
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化LED2的GPIO
GPIO_WriteBit(GPIOC, GPIO_Pin_5, Bit_RESET); //将LED2默认状态设置为熄灭
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitLED
* 函数功能:初始化LED模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void InitLED(void)
{
ConfigLEDGPIO(); //配置LED的GPIO
}
/*********************************************************************************************************
* 函数名称:LEDFlicker
* 函数功能:LED闪烁函数
* 输入参数:cnt
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:LEDFlicker在Proc2msTask中调用,cnt为250时表示每500ms更改一次LED状态
*********************************************************************************************************/
void LEDFlicker(u16 cnt)
{
static u16 s_iCnt; //定义静态变量s_iCnt作为计数器
s_iCnt++; //计数器的计数值加1
if(s_iCnt >= cnt) //计数器的计数值大于cnt
{
s_iCnt = 0; //重置计数器的计数值为0
//LED1状态取反,实现LED0闪烁
GPIO_WriteBit(GPIOC, GPIO_Pin_4, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_4)));
//LED2状态取反,实现LED1闪烁
GPIO_WriteBit(GPIOC, GPIO_Pin_5, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_5)));
}
}
2.GPIO与按键:
/*********************************************************************************************************
* 函数名称:ConfigKeyOneGPIO
* 函数功能:配置按键的GPIO
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static void ConfigKeyOneGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
//使能RCC相关时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOC的时钟
//配置PC1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置输入类型
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
//配置PC2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置输入类型
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化GPIO
//配置PA0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置输入类型
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据参数初始化GPIO
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitKeyOne
* 函数功能:初始化KeyOne模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void InitKeyOne(void)
{
ConfigKeyOneGPIO(); //配置按键的GPIO
s_arrKeyDownLevel[KEY_NAME_KEY1] = KEY_DOWN_LEVEL_KEY1; //按键KEY1按下时为低电平
s_arrKeyDownLevel[KEY_NAME_KEY2] = KEY_DOWN_LEVEL_KEY2; //按键KEY2按下时为低电平
s_arrKeyDownLevel[KEY_NAME_KEY3] = KEY_DOWN_LEVEL_KEY3; //按键KEY3按下时为低电平
}
/*********************************************************************************************************
* 函数名称:ScanKeyOne
* 函数功能:按键扫描,每10ms调用一次
* 输入参数:keyName-按键名,OnKeyOneUp-按键弹起响应函数的指针,OnKeyOneDown-按键按下响应函数的指针
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:如果s_arrKeyDownLevel[keyName] = 0xFF,对s_arrKeyDownLevel[keyName]直接取反得出的是256,而非0
* 正确的做法是(u8)(~s_arrKeyDownLevel[keyName]),这样得出的才是0。
*********************************************************************************************************/
void ScanKeyOne(u8 keyName, void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{
static u8 s_arrKeyVal[KEY_NAME_MAX]; //定义一个u8类型的数组,用于存放按键的数值
static u8 s_arrKeyFlag[KEY_NAME_MAX]; //定义一个u8类型的数组,用于存放按键的标志位
s_arrKeyVal[keyName] = s_arrKeyVal[keyName] << 1; //左移一位
switch (keyName)
{
case KEY_NAME_KEY1:
s_arrKeyVal[keyName] = s_arrKeyVal[keyName] | KEY1; //按下/弹起时,KEY1为0/1
break;
case KEY_NAME_KEY2:
s_arrKeyVal[keyName] = s_arrKeyVal[keyName] | KEY2; //按下/弹起时,KEY2为0/1
break;
case KEY_NAME_KEY3:
s_arrKeyVal[keyName] = s_arrKeyVal[keyName] | KEY3; //按下/弹起时,KEY3为0/1
break;
default:
break;
}
//按键标志位的值为TRUE时,判断是否有按键有效按下
if(s_arrKeyVal[keyName] == s_arrKeyDownLevel[keyName] && s_arrKeyFlag[keyName] == TRUE)
{
(*OnKeyOneDown)(); //执行按键按下的响应函数
s_arrKeyFlag[keyName] = FALSE; //表示按键处于按下状态,按键标志位的值更改为FALSE
}
//按键标志位的值为FALSE时,判断是否有按键有效弹起
else if(s_arrKeyVal[keyName] == (u8)(~s_arrKeyDownLevel[keyName]) && s_arrKeyFlag[keyName] == FALSE)
{
(*OnKeyOneUp)(); //执行按键弹起的响应函数
s_arrKeyFlag[keyName] = TRUE; //表示按键处于弹起状态,按键标志位的值更改为TRUE
}
}
3.串口通信
简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接 当只需单向的数据传输时,可以只接一根通信线
当电平标准不一致时,需要加电平转换芯片
TTL电平:+3.3V或+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
波特率:串口通信的速率
起始位:标志一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位:用于数据验证,根据数据位计算得来
停止位:用于数据帧间隔,固定为高电平
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器 USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里 自带波特率发生器,最高达4.5Mbits/s 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2) 可选校验位(无校验/奇校验/偶校验) 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
计算公式:波特率 = fPCLK2/1 / (16 * DIV)
* 内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitUARTBuf
* 函数功能:初始化串口缓冲区,包括发送缓冲区和接收缓冲区
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static void InitUARTBuf(void)
{
i16 i;
for(i = 0; i < UART1_BUF_SIZE; i++)
{
s_arrSendBuf[i] = 0;
s_arrRecBuf[i] = 0;
}
InitQueue(&s_structUARTSendCirQue, s_arrSendBuf, UART1_BUF_SIZE);
InitQueue(&s_structUARTRecCirQue, s_arrRecBuf, UART1_BUF_SIZE);
}
/*********************************************************************************************************
* 函数名称:WriteReceiveBuf
* 函数功能:写数据到串口接收缓冲区
* 输入参数:d,待写入串口接收缓冲区的数据
* 输出参数:void
* 返 回 值:写入数据成功标志,0-不成功,1-成功
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static u8 WriteReceiveBuf(u8 d)
{
u8 ok = 0; //写入数据成功标志,0-不成功,1-成功
ok = EnQueue(&s_structUARTRecCirQue, &d, 1);
return ok; //返回写入数据成功标志,0-不成功,1-成功
}
/*********************************************************************************************************
* 函数名称:ReadSendBuf
* 函数功能:读取串口发送缓冲区中的数据
* 输入参数:p,读出来的数据存放的首地址
* 输出参数:p,读出来的数据存放的首地址
* 返 回 值:读取数据成功标志,0-不成功,1-成功
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static u8 ReadSendBuf(u8 *p)
{
u8 ok = 0; //读取数据成功标志,0-不成功,1-成功
ok = DeQueue(&s_structUARTSendCirQue, p, 1);
return ok; //返回读取数据成功标志,0-不成功,1-成功
}
/*********************************************************************************************************
* 函数名称:ConfigUART
* 函数功能:配置串口相关的参数,包括GPIO、RCC、USART和NVIC
* 输入参数:bound,波特率
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static void ConfigUART(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
USART_InitTypeDef USART_InitStructure; //USART_InitStructure用于存放USART的参数
NVIC_InitTypeDef NVIC_InitStructure; //NVIC_InitStructure用于存放NVIC的参数
//使能RCC相关时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的时钟
//配置TX的GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //设置TX的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置TX的模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置TX的I/O口输出速度
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据参数初始化TX的GPIO
//配置RX的GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //设置RX的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置RX的模式
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据参数初始化RX的GPIO
//配置USART的参数
USART_StructInit(&USART_InitStructure); //初始化USART_InitStructure
USART_InitStructure.USART_BaudRate = bound; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置数据字长度
USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //设置奇偶校验位
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //设置模式
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //设置硬件流控制模式
USART_Init(USART1, &USART_InitStructure); //根据参数初始化USART1
//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //中断通道号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure); //根据参数初始化NVIC
//使能USART1及其中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能接收缓冲区非空中断
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); //使能发送缓冲区空中断
USART_Cmd(USART1, ENABLE); //使能USART1
s_iUARTTxSts = UART_STATE_OFF; //串口发送数据状态设置为未发送数据
}
/*********************************************************************************************************
* 函数名称:EnableUARTTx
* 函数功能:使能串口发送,在WriteUARTx中调用,即每次发送数据之后需要调用这个函数来使能发送中断
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:s_iUARTTxSts = UART_STATE_ON;这句话必须放在USART_ITConfig之前,否则会导致中断打开无法执行
*********************************************************************************************************/
static void EnableUARTTx(void)
{
s_iUARTTxSts = UART_STATE_ON; //串口发送数据状态设置为正在发送数据
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); //使能发送中断
}
/*********************************************************************************************************
* 函数名称:SendCharUsedByFputc
* 函数功能:发送字符函数,专由fputc函数调用
* 输入参数:ch,待发送的字符
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
static void SendCharUsedByFputc(u16 ch)
{
USART_SendData(USART1, (u8)ch);
//等待发送完毕
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
/*********************************************************************************************************
* 函数名称:USART1_IRQHandler
* 函数功能:USART1中断服务函数
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void USART1_IRQHandler(void)
{
u8 uData = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收缓冲区非空中断
{
NVIC_ClearPendingIRQ(USART1_IRQn); //清除USART1中断挂起
uData = USART_ReceiveData(USART1); //将USART1接收到的数据保存到uData
WriteReceiveBuf(uData); //将接收到的数据写入接收缓冲区
}
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET) //溢出错误标志为1
{
USART_ClearFlag(USART1, USART_FLAG_ORE); //清除溢出错误标志
USART_ReceiveData(USART1); //读取USART_DR
}
if(USART_GetITStatus(USART1, USART_IT_TXE)!= RESET) //发送缓冲区空中断
{
USART_ClearITPendingBit(USART1, USART_IT_TXE); //清除发送中断标志
NVIC_ClearPendingIRQ(USART1_IRQn); //清除USART1中断挂起
ReadSendBuf(&uData); //读取发送缓冲区的数据到uData
USART_SendData(USART1, uData); //将uData写入USART_DR
if(QueueEmpty(&s_structUARTSendCirQue)) //当发送缓冲区为空时
{
s_iUARTTxSts = UART_STATE_OFF; //串口发送数据状态设置为未发送数据
USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //关闭串口发送缓冲区空中断
}
}
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitUART1
* 函数功能:初始化UART模块
* 输入参数:bound,波特率
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void InitUART1(u32 bound)
{
InitUARTBuf(); //初始化串口缓冲区,包括发送缓冲区和接收缓冲区
ConfigUART(bound); //配置串口相关的参数,包括GPIO、RCC、USART和NVIC
}
/*********************************************************************************************************
* 函数名称:WriteUART1
* 函数功能:写串口,即写数据到的串口发送缓冲区
* 输入参数:pBuf,要写入数据的首地址,len,期望写入数据的个数
* 输出参数:void
* 返 回 值:成功写入数据的个数,不一定与形参len相等
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
u8 WriteUART1(u8 *pBuf, u8 len)
{
u8 wLen = 0; //实际写入数据的个数
wLen = EnQueue(&s_structUARTSendCirQue, pBuf, len);
if(wLen < UART1_BUF_SIZE)
{
if(s_iUARTTxSts == UART_STATE_OFF)
{
EnableUARTTx();
}
}
return wLen; //返回实际写入数据的个数
}
/*********************************************************************************************************
* 函数名称:ReadUART1
* 函数功能:读串口,即读取串口接收缓冲区中的数据
* 输入参数:pBuf,读取的数据存放的首地址,len,期望读取数据的个数
* 输出参数:pBuf,读取的数据存放的首地址
* 返 回 值:成功读取数据的个数,不一定与形参len相等
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
u8 ReadUART1(u8 *pBuf, u8 len)
{
u8 rLen = 0; //实际读取数据长度
rLen = DeQueue(&s_structUARTRecCirQue, pBuf, len);
return rLen; //返回实际读取数据的长度
}
/*********************************************************************************************************
* 函数名称:fputc
* 函数功能:重定向函数
* 输入参数:ch,f
* 输出参数:void
* 返 回 值:int
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
int fputc(int ch, FILE* f)
{
SendCharUsedByFputc((u8) ch); //发送字符函数,专由fputc函数调用
return ch; //返回ch
}
4.定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
* 函数名称:ConfigTimer2
* 函数功能:配置TIM2
* 输入参数:arr-自动重装值,psc-预分频器值
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:APB1时钟为36MHz,TIM2-TIM5时钟选择为APB1的2倍,因此,TIM2-TIM5时钟为72MHz
*********************************************************************************************************/
static void ConfigTimer2(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//TIM_TimeBaseStructure用于存放定时器的参数
NVIC_InitTypeDef NVIC_InitStructure; //NVIC_InitStructure用于存放NVIC的参数
//使能RCC相关时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2的时钟
//配置TIM2
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据参数初始化定时器
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //使能定时器的更新中断
//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //中断通道号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure); //根据参数初始化NVIC
TIM_Cmd(TIM2, ENABLE); //使能定时器
}
/*********************************************************************************************************
* 函数名称:ConfigTimer5
* 函数功能:配置TIM5
* 输入参数:arr-自动重装值,psc-预分频器值
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:APB1时钟为36MHz,TIM2-TIM5时钟选择为APB1的2倍,因此,TIM2-TIM5时钟为72MHz
*********************************************************************************************************/
static void ConfigTimer5(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//TIM_TimeBaseStructure用于存放定时器的参数
NVIC_InitTypeDef NVIC_InitStructure; //NVIC_InitStructure用于存放NVIC的参数
//使能RCC相关时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5的时钟
//配置TIM5
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据参数初始化定时器
TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE); //使能定时器的更新中断
//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //中断通道号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure); //根据参数初始化NVIC
TIM_Cmd(TIM5, ENABLE); //使能定时器
}
/*********************************************************************************************************
* 函数名称:TIM2_IRQHandler
* 函数功能:TIM2中断服务函数
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:每毫秒进入一次中断服务函数
*********************************************************************************************************/
void TIM2_IRQHandler(void)
{
static u16 s_iCnt2 = 0; //定义一个静态变量s_iCnt2作为2ms计数器
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断定时器更新中断是否发生
{
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update); //清除定时器更新中断标志
}
s_iCnt2++; //2ms计数器的计数值加1
if(s_iCnt2 >= 2) //2ms计数器的计数值大于或等于2
{
s_iCnt2 = 0; //重置2ms计数器的计数值为0
s_i2msFlag = TRUE; //将2ms标志位的值设置为TRUE
}
}
/*********************************************************************************************************
* 函数名称:TIM5_IRQHandler
* 函数功能:TIM5中断服务函数
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:每毫秒进入一次中断服务函数
*********************************************************************************************************/
void TIM5_IRQHandler(void)
{
static i16 s_iCnt1000 = 0; //定义一个静态变量s_iCnt1000作为1s计数器
if (TIM_GetITStatus(TIM5, TIM_IT_Update) == SET) //判断定时器更新中断是否发生
{
TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); //清除定时器更新中断标志
}
s_iCnt1000++; //1000ms计数器的计数值加1
if(s_iCnt1000 >= 1000) //1000ms计数器的计数值大于或等于1000
{
s_iCnt1000 = 0; //重置1000ms计数器的计数值为0
s_i1secFlag = TRUE; //将1s标志位的值设置为TRUE
}
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitTimer
* 函数功能:初始化Timer模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:使用的TIM2-7由APB1(36MHz)预分频后输出。如果预分频为1,则由APB1*1输出,否则由APB1*2(72MHz)输出
*********************************************************************************************************/
void InitTimer(void)
{
ConfigTimer2(999, 71); //72MHz/(71+1)=1MHz,由0计数到999为1ms
ConfigTimer5(999, 71); //72MHz/(71+1)=1MHz,由0计数到999为1ms
}
static void Proc1SecTask(void)
{
if(Get1SecFlag()) //判断1s标志状态
{
printf("This is the first STM32F103 Project, by Zhangsan\r\n");
Clr1SecFlag(); //清除1s标志
}
}
5.系统节拍时钟
主要用于操作系统;这里只用于普通的定时器。
static __IO u32 s_iTimDelayCnt = 0; //延时计数器s_iTimDelayCnt的初始值为0
/*********************************************************************************************************
* 内部函数声明
*********************************************************************************************************/
static void TimDelayDec(void); //延时计数
/*********************************************************************************************************
* 内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:TimDelayDec
* 函数功能:延时计数
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:在SysTick中断服务函数SysTick_Handler中调用
*********************************************************************************************************/
static void TimDelayDec(void)
{
if(s_iTimDelayCnt != 0) //延时计数器的数值不为0
{
s_iTimDelayCnt--; //延时计数器的数值减1
}
}
/*********************************************************************************************************
* 函数名称:SysTick_Handler
* 函数功能:SysTick中断服务函数
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void SysTick_Handler(void)
{
TimDelayDec(); //延时计数函数
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitSysTick
* 函数功能:初始化SysTick模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:SystemCoreClock / 1000 1ms中断一次(计数1000次为1s,每计一次位1/1000s=1ms)
* SystemCoreClock / 4000 0.25ms中断一次(计数4000次为1s,每计一次位1/4000s=0.25ms)
* SystemCoreClock / 100000 10us中断一次(计数100000次为1s,每计一次位1/100000s=10us)
* SystemCoreClock / 1000000 1us中断一次(计数1000000次为1s,每计一次位1/1000000s=1us)
*********************************************************************************************************/
void InitSysTick( void )
{
if (SysTick_Config(SystemCoreClock / 1000)) //配置系统滴答定时器1ms中断一次
{
while(1) //错误发生的情况下,进入死循环
{
}
}
}
/*********************************************************************************************************
* 函数名称:DelayNms
* 函数功能:毫秒级延时函数
* 输入参数:nms
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void DelayNms(__IO u32 nms)
{
s_iTimDelayCnt = nms; //将延时计数器s_iTimDelayCnt的数值赋为nms
while(s_iTimDelayCnt != 0) //延时计数器的数值为0时,表示延时了nms,跳出while语句
{
}
}
/*********************************************************************************************************
* 函数名称:DelayNus
* 函数功能:微秒级延时函数
* 输入参数:nus
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:经测试,100us和200us基本准确
*********************************************************************************************************/
void DelayNus(__IO u32 nus)
{
u32 s_iTimCnt = nus; //定义一个变量s_iTimCnt作为延时计数器,赋值为nus
u16 i; //定义一个变量作为循环计数器
while(s_iTimCnt != 0) //延时计数器s_iTimCnt的值不为0
{
for(i = 0; i < 7; i++) //空循环,产生延时功能
{
}
s_iTimCnt--; //成功延时1us,变量s_iTimCnt减1
}
}
6.RCC
时钟控制器,开启相应的外设时钟。(满足低功耗应用)
通过对HSE时钟的倍频,对AHB总线时钟的分频,对APB1总线和APB2总线时钟频率进行分配。
*******************************************************************************************************
* 内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:ConfigRCC
* 函数功能:配置RCC
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:配置的时钟如下:
* (0)外部晶振HSE = 8MHz
* (1)PLLCLK(PLL时钟) = HSE*9 = 72MHz
* (2)SYSCLK(系统时钟) = PLLCLK = 72MHz
* (3)HCLK(AHB总线时钟) = SYSCLK = 72MHz
* (4)PCLK1(APB1总线时钟)= HCLK/2 = 36MHz
* (5)PCLK2(APB2总线时钟)= HCLK = 72MHz
* (6)ADCCLK(ADC时钟) = PCLK2/4 = 72/4 = 18MHz
*********************************************************************************************************/
static void ConfigRCC(void)
{
ErrorStatus HSEStartUpStatus; //定义枚举变量HSEStartUpStatus,用来标志外部高速晶振的状态
RCC_DeInit(); //将外设RCC寄存器重设为默认值
RCC_HSEConfig(RCC_HSE_ON); //使能外部高速晶振
HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待外部高速晶振稳定
if(HSEStartUpStatus == SUCCESS) //外部高速晶振成功稳定
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能flash预读取缓冲区
FLASH_SetLatency(FLASH_Latency_2); //设置代码延时值,FLASH_Latency_2,2延时周期
RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置高速AHB时钟(HCLK),RCC_SYSCLK_Div1,HCLK = SYSCLK
RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速APB2时钟(PCLK2),RCC_HCLK_Div1,PCLK2 = HCLK
RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速APB1时钟(PCLK1),RCC_HCLK_Div2,PCLK1 = HCLK/2
//RCC_ADCCLKConfig(RCC_PCLK2_Div4); //设置ADC时钟(ADCCLK),RCC_PCLK2_Div4,ADCCLK = PCLK2/4
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//设置PLL时钟源及倍频系数,PLLCLK = 8MHz*9 = 72MHz
RCC_PLLCmd(ENABLE); //使能PLL
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) //等待锁相环输出稳定
{
}
//设置HSI/HSE/PLL为系统时钟
//RCC_SYSCLKSource_HSI
//RCC_SYSCLKSource_HSE
//RCC_SYSCLKSource_PLLCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //将锁相环输出设置为系统时钟
//等待HSI/HSE/PLL成功用于系统时钟的时钟源
//0x00-HSI作为系统时钟
//0x04-HSE作为系统时钟
//0x08-PLL作为系统时钟
while(RCC_GetSYSCLKSource() != 0x08) //等待PLL成功用于系统时钟的时钟源
{
}
}
}
/*********************************************************************************************************
* API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称:InitRCC
* 函数功能:初始化RCC模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 创建日期:2018年01月01日
* 注 意:
*********************************************************************************************************/
void InitRCC(void)
{
ConfigRCC(); //配置RCC
}