自定义简单的固件库
mystm32f10x.h
#ifndef __MYSTM32F10X_H
#define __MYSTM32F10X_H
// 用来存放STM32寄存器映射的代码
// 外设
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
// RCC基地址
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
// GPIOB基地址
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
//#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
//#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
//#define GPIOB_IDR *(unsigned int*)(GPIOB_BASE + 0x08)
//#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
//#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE + 0x10)
//#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE + 0x14)
//#define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE + 0x18)
typedef unsigned int unit32_t;
typedef unsigned short unit16_t;
/* ---------将GPIO定义成结构体------------- */
typedef struct
{
unit32_t CRL;
unit32_t CRH;
unit32_t IDR;
unit32_t ODR;
unit32_t BSRR;
unit32_t BRR;
unit32_t LCKR;
}GPIO_TypeDef;
// 强制类型转换
#define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
/* ---------RCC时钟结构体------------- */
typedef struct
{
unit32_t CR;
unit32_t CFGR;
unit32_t CIR;
unit32_t APB2RSTR;
unit32_t APB1RSTR;
unit32_t AHBENR;
unit32_t APB2ENR;
unit32_t APB1ENR;
unit32_t BDCR;
unit32_t CSR;
}RCC_TypeDef;
#define RCC ((RCC_TypeDef*)RCC_BASE)
#endif /*__MYSTM32F10X_H*/
mystm32f10x_gpio.h
#ifndef __STM32F10x_GPIO_H
#define __STM32F10x_GPIO_H
#include "stm32f10x.h"
// GPIO引脚宏定义
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
// 定义GPIO初始化结构体
typedef struct
{
uint16_t GPIO_Pin; // 选择要配置的引脚
uint16_t GPIO_Speed; // 选择GPIO引脚的速率
uint16_t GPIO_Mode; // 选择GPIO引脚的工作模式
}GPIO_InitTypeDef;
/*----------GPIO输出速率枚举定义-----------*/
typedef enum
{
GPIO_Speed_10MHZ = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
/*-----------GPIO工作模式枚举定义---------------*/
typedef enum
{
GPIO_Mode_AIN = 0x00, // 模拟输入
GPIO_Mode_IN_FLOATIN = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
}GPIO_Mode_TypeDef;
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
#endif /* __STM32F10x_GPIO_H */
mystm32f10x_gpio.c
#include "stm32f10x_gpio.h"
// 端口复位函数
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BSRR |= GPIO_Pin;
}
// 清除
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BRR |= GPIO_Pin;
}
// 初始化
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
/*---------------------- GPIO 模式配置 --------------------------*/
// 把输入参数GPIO_Mode的低四位暂存在currentmode
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
// bit4是1表示输出,bit4是0则是输入
// 判断bit4是1还是0,即首选判断是输入还是输出模式
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
{
// 输出模式则要设置输出速度
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
}
/*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
// 配置端口低8位,即Pin0~Pin7
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
{
// 先备份CRL寄存器的值
tmpreg = GPIOx->CRL;
// 循环,从Pin0开始配对,找出具体的Pin
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
// pos的值为1左移pinpos位
pos = ((uint32_t)0x01) << pinpos;
// 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
//若currentpin=pos,则找到使用的引脚
if (currentpin == pos)
{
// pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
pos = pinpos << 2;
//把控制这个引脚的4个寄存器位清零,其它寄存器位不变
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
// 向寄存器写入将要配置的引脚的模式
tmpreg |= (currentmode << pos);
// 判断是否为下拉输入模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
// 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
GPIOx->BRR = (((uint32_t)0x01) << pinpos);
}
else
{
// 判断是否为上拉输入模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
// 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
}
}
}
}
// 把前面处理后的暂存值写入到CRL寄存器之中
GPIOx->CRL = tmpreg;
}
/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
// 配置端口高8位,即Pin8~Pin15
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
// // 先备份CRH寄存器的值
tmpreg = GPIOx->CRH;
// 循环,从Pin8开始配对,找出具体的Pin
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
// pos与输入参数GPIO_PIN作位与运算
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
//若currentpin=pos,则找到使用的引脚
if (currentpin == pos)
{
//pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
pos = pinpos << 2;
//把控制这个引脚的4个寄存器位清零,其它寄存器位不变
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
// 向寄存器写入将要配置的引脚的模式
tmpreg |= (currentmode << pos);
// 判断是否为下拉输入模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
// 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
// 判断是否为上拉输入模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
// 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
// 把前面处理后的暂存值写入到CRH寄存器之中
GPIOx->CRH = tmpreg;
}
}
按键设置
bsp_key.h
#ifndef __BSP_KEY_H
#define __BSP_KEY_H
#include "stm32f10x.h"
// 宏定义按键1
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
// 宏定义按键2
#define KEY2_GPIO_PIN GPIO_Pin_13
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY_ON 1
#define KEY_OFF 0
void KEY_GPIO_Config(void);
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
#endif // __BSP_KEY_H
bsp_key.c
#include "bsp_key.h"
// 按钮配置
void KEY_GPIO_Config(void)
{
// KEY1
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);
GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct);
// KEY2
GPIO_InitTypeDef GPIO_InitStruct2;
RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK, ENABLE);
GPIO_InitStruct2.GPIO_Pin = KEY2_GPIO_PIN;
GPIO_InitStruct2.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式 /一般是上下拉输入
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct2);
}
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
// 检测输出是否为高电平
if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON)
{
// 松手检测
while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON);
return KEY_ON;
}else return KEY_OFF;
}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:使用按键1控制led0,按键2控制led1
按键1引脚:PA0
按键2引脚:PC13
led0引脚:PB0
led1引脚:PB1
***********************/
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"
#define GPIOB_ODR_Addr (GPIOB_BASE + 0x0C)
#define PBout(n) *(unsigned int*)((GPIOB_ODR_Addr & 0xF0000000) + 0x02000000 +((GPIOB_ODR_Addr & 0x00FFFFFF) << 5) + (n<<2))
#define GPIOA_IDR_Addr (GPIOA_BASE + 0x08)
#define PAin(n) *(unsigned int*)((GPIOA_IDR_Addr & 0xF0000000) + 0x02000000 +((GPIOA_IDR_Addr & 0x00FFFFFF) << 5) + (n<<2))
int main(void)
{
LED_GPIO_Config(); // 配置LED
KEY_GPIO_Config(); // 配置KEY
while(1)
{
// 按键1控制LED0
if(Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
{
LED0_TOGGLE; // LED0翻转
}
if(Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
{
LED1_TOGGLE; // LED1翻转
}
}
}
无阻塞按键函数
uint16_t count = 0; // 计数
uint8_t flag = 0; // 记住这次的状态
uint8_t flag2 = 1; // 记住上次的状态
void key_scan(void)
{
uint8_t temp = GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN); // 按键按下时为1,弹开时0
count++;
switch(flag)
{
case 0:
if(count>10 && temp == 1)
{
LED0(ON);
count = 0;
flag2 = 1;
}else if(temp == 0 && flag2 == 1)
{
flag = 1;
}
break;
case 1:
if(count>10 && temp == 1)
{
LED0(OFF);
count = 0;
flag2 = 0;
}else if(temp == 0 && flag2 == 0)
{
flag = 0;
}
break;
}
}
无阻塞弹起发送数据
extern uint8_t sw1_flag;
/**
* @brief 检测SW1按键
* @param
*
* @retval
*/
void Scan_sw1(void)
{
#define KEY_WAIT_INPUT 0
#define KEY_CONFIRM_INPUT 1
#define KEY_RE_CONFIRM_INPUT 2
#define KEY_RELEASE 3
static uint16_t key_time=0;
static uint8_t key_state=0;
static uint8_t signal=0;
uint8_t key_data;
GPIO_SetBits(GPIOA, GPIO_Pin_0);
key_data = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
switch(key_state)
{
case KEY_WAIT_INPUT://wait key input
if(key_data == 0)
key_state = KEY_CONFIRM_INPUT;
break;
case KEY_CONFIRM_INPUT://confirm key input
if(key_data == 0)
{
key_state = KEY_RE_CONFIRM_INPUT;
}
else
{
key_state = KEY_WAIT_INPUT;
}
break;
case KEY_RE_CONFIRM_INPUT://re-confirm key_input
{
static uint8_t cnt=0;
if(key_data == 0)
{
if(key_time++ > 20)
{
key_time = 0;
cnt = 0;
signal=1;
//按键标志
//key_state = KEY_RE_CONFIRM_INPUT;
}
}
else
{
if(signal)
{
if(cnt++ > 5)
{
cnt=0;
signal=0;
sw1_flag = 1;
key_state = KEY_WAIT_INPUT;
}
}
}
}
break;
default:
break;
}
}
main.c
uint8_t sw1_flag;
extern void Scan_sw1(void);
extern uint8_t Scan_key_flag;
TIM3_Int_Init(73,1000); // 延时毫秒
while(1)
{
if(Scan_key_flag) // 1ms中断
{
Scan_key_flag=0;
Scan_sw1();
}
if(sw1_flag)
{
sw1_flag=0;
USART1_DMASendString("key_status1\n",sizeof("key_status1"));
}
}
自定设置系统时钟
bsp_rccclkconfig.h文件
#ifndef __BSP_RCCCLKCONFIG_H
#define __BSP_RCCCLKCONFIG_H
#include "stm32f10x.h"
void HSE_SetSysClk(uint32_t RCC_PLLMul_x);
void HSI_SetSysClk(uint32_t RCC_PLLMul_x);
void MCO_GPIO_Config(void);
#endif //__BSP_RCCCLKCONFIG_H
bsp_rccclkconfig.c文件
#include "bsp_rccclkconfig.h"
// RCC_PLLMul_x where x:[2,16]
// 自定HSE系统时钟
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
// 错误状态标志
ErrorStatus HSEStatus;
RCC_DeInit(); // 将RCC寄存器复位成默认状态
// 使能HSE
RCC_HSEConfig(RCC_HSE_ON);
// 等待HSE使能成功,成功返回1,错误返回0
HSEStatus = RCC_WaitForHSEStartUp();
if(HSEStatus == SUCCESS)
{
// 启动成功
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// 设置flase等待周期 设置两个周期等待
FLASH_SetLatency(FLASH_Latency_2);
// 配置三个总线的分屏因子
RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK
RCC_PCLK1Config(RCC_SYSCLK_Div2); // PCLK2
RCC_PCLK2Config(RCC_SYSCLK_Div1); // PCLK1
// 配置锁相环
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x); // 配置PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLCmd(ENABLE); // 使能
// 等待PLL稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){};
// 选择PLLCLK作为系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 等待PLLCLK切换成系统时钟
while(RCC_GetSYSCLKSource() != 0x08){};
}else
{
// 如果HSE启动失败,用户可以在这里添加处理错误的代码
}
}
// 自定HSI系统时钟
void HSI_SetSysClk(uint32_t RCC_PLLMul_x)
{
// 错误状态标志
__IO uint32_t HSIStatus;
RCC_DeInit(); // 将RCC寄存器复位成默认状态
// 使能HSE
RCC_HSICmd(ENABLE);
// 等待HSI使能成功,成功返回1,错误返回0
HSIStatus = RCC->CR & RCC_CR_HSIRDY;
if(HSIStatus == SUCCESS)
{
// 启动成功
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// 设置flase等待周期 设置两个周期等待
FLASH_SetLatency(FLASH_Latency_2);
// 配置三个总线的分屏因子 HCLK = PCLK2 PCLK1/2 = 72M
RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK 72M
RCC_PCLK1Config(RCC_SYSCLK_Div2); // PCLK1 低速36M
RCC_PCLK2Config(RCC_SYSCLK_Div1); // PCLK2 高速72M
// 配置锁相环
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x); // 配置PLLCLK = HSI * RCC_PLLMul_x
RCC_PLLCmd(ENABLE); // 使能
// 等待PLL稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){};
// 选择PLLCLK作为系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 等待PLLCLK切换成系统时钟
while(RCC_GetSYSCLKSource() != 0x08){};
}else
{
// 如果HSI启动失败,用户可以在这里添加处理错误的代码
}
}
// 初始化MCO的GPIO 用于向外输出频率
void MCO_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
man.c文件
int main(void)
{
HSE_SetSysClk(RCC_PLLMul_9); // 自定设置系统时钟
// 配置MCO 用于向外输出频率
MCO_GPIO_Config(); // 初始化引脚
RCC_MCOConfig(RCC_MCO_SYSCLK); // 使能
}
野火教程链接https://www.bilibili.com/video/BV1yW411Y7Gw?p=25
F103ZET6核心板.zip
GPIO外部中断设置-EXTI中断-对按钮操作
使用EXTI来实现按键控制led亮-灭
bsp_exti.h
#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H
#include "stm32f10x.h"
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
#define KEY1_INT_GPIO_PORT GPIOA
#define KEY1_INT_GPIO_CLK RCC_APB2Periph_GPIOA
void EXTI_NVIC_Config(void);
void EXIT_Key_Config(void);
#endif // __BSP_EXTI_H
bsp_exti.c
#include "bsp_exti.h"
// 配置中断优先级 static表示静态作用域,只能被bsp_exti文件内调用
static void EXTI_NVIC_Config(void)
{
/* 初始化NVIC,用于处理中断 */
// NVIC配置的变量值设置
NVIC_InitTypeDef NVIC_InitStrct;
// 中断分组设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 组1
NVIC_InitStrct.NVIC_IRQChannel = EXTI0_IRQn; // 配置中断源
NVIC_InitStrct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStrct.NVIC_IRQChannelSubPriority = 1; // 响应优先级
NVIC_InitStrct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStrct); // 进行初始化
}
// 外部中断 GPIO的外部中断由EXTI控制
void EXIT_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct; // GPIO变量设置
EXTI_InitTypeDef EXTI_InitStruct; // EXTI变量设置
// 配置中断优先级
EXTI_NVIC_Config();
// 初始化要连接到EXTI的GPIO
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK, ENABLE); // 打开GPIO对应的时钟
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_InitStruct.GPIO_Pin = KEY1_INT_GPIO_PIN;
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStruct);
// 初始化EXTI 用于产生中断/事件
RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, ENABLE); // 打开EXTI对应的时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 输入线
EXTI_InitStruct.EXTI_Line = EXTI_Line0; // 线为0
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 模式配置成中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_InitStruct.EXTI_LineCmd = ENABLE; // 使能中断屏蔽寄存器设为1
EXTI_Init(&EXTI_InitStruct); // 将设置的值进行配置
// 编写中断服务函数
// 中断函数都在stm32f10x_it 该中断为 EXTI0_IRQHandler
}
stm32f10x_it.c
#include "bsp_led.h"
#include "bsp_exti.h"
// EXTI0中断函数
void EXTI0_IRQHandler(void)
{
// 判断中断标志位是否为1
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
LED0_TOGGLE;
}
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:使用EXTI来实现按键控制led亮-灭
KEY引脚:PA0
LED引脚:PC0
***********************/
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_rccclkconfig.h"
#include "bsp_exti.h"
#define GPIOB_ODR_Addr (GPIOB_BASE + 0x0C)
#define PBout(n) *(unsigned int*)((GPIOB_ODR_Addr & 0xF0000000) + 0x02000000 +((GPIOB_ODR_Addr & 0x00FFFFFF) << 5) + (n<<2))
#define GPIOA_IDR_Addr (GPIOA_BASE + 0x08)
#define PAin(n) *(unsigned int*)((GPIOA_IDR_Addr & 0xF0000000) + 0x02000000 +((GPIOA_IDR_Addr & 0x00FFFFFF) << 5) + (n<<2))
int main(void)
{
EXIT_Key_Config(); // 初始化
LED_GPIO_Config(); // 配置LED
while(1)
{
}
}
野火视频教程:https://www.bilibili.com/video/BV1yW411Y7Gw?p=29
SysTick系统滴答定时器
SysTick寄存器介绍
SysTick 初始化函数
/**
* @brief 启动系统滴答定时器 SysTick
* @param 无
* @retval 无
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms 中断一次
* SystemFrequency / 100000 10us 中断一次
* SystemFrequency / 1000000 1us 中断一次
*/
if (SysTick_Config(SystemCoreClock / 100000)) {
/* Capture error */
while (1);
}
}
SysTick 初始化函数由用户编写,里面调用了 SysTick_Config() 这个固件库函数,通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断。
SysTick中断时间计算
SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,当重装载寄存器中的值VALUELOAD 减到0的时候,产生中断 , 可知中断一次的时间 TINT = VALUELOAD * TDEC = VALUELOAD/CLKAHB ,其中 CLKAHB = 72MHZ 。 如果设置VALUELOAD为72,那中断一次的时间 TINT = 72/72M = 1us。不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他的任务。
SysTick_Config(SystemCoreClock / 100000))
SysTick_Config() 的形我们配置为 SystemCoreClock / 100000 = 72M/100000 = 720,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中断一次的时间TINT = 720/72M = 10us。
SysTick定时器时间计算
t:一个计数循环的时间,跟reload和CLK有关
CLK:72M或者9M,有CTRL寄存器配置
RELOAD:24位,用户自己配置
t = reload (1/clk)
如:
clk = 72M时,t = 72(1/72M) = 1us
clk = 72M时,t = 72000*(1/72M) = 1ms
时间单位换算
1s = 1000ms = 1000 000us = 1000 000 000ns
SysTick配置库函数
SysTick中断优先级
1.STM32里面无论是内核还是外设都是使用4个二进制位来表示中断优先级。
2.中断优先级的分组对内核与外设同样适用。当比较的时候,只需要把内核外设的中断优先级的四个为按照外设的中断优先级来分组来解析即可,即认为的分出抢占优先级和子优先级。
对应的野火教程:https://www.bilibili.com/video/BV1yW411Y7Gw?p=30
使用SysTick中断来实现定时器
bsp_systick.h
#ifndef __BSP_SYSTICK_H
#define __BSP_SYSTICK_H
#include "stm32f10x.h"
#include "core_cm3.h"
void SysTick_Delay_us(uint32_t us);
void SysTick_Delay_ms(uint32_t ms);
#endif // __BSP_SYSTICK_H
bsp_systick.c
#include "bsp_systick.h"
// 定时1us
void SysTick_Delay_us(uint32_t us)
{
// 初始化
SysTick_Config(72); // 配置1us的时间
for(uint16_t i=0; i<us; i++)
{
// 当计数器减到0时,则控制及状态寄存器的第16位为1即COUNTFLAG=1
while(!((SysTick->CTRL) & (1<<16)));
}
// 关闭定时器
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
// 定时1ms
void SysTick_Delay_ms(uint32_t ms)
{
// 初始化
SysTick_Config(72000); // 配置1ms的时间
for(uint16_t i=0; i<ms; i++)
{
//
while(!((SysTick->CTRL) & (1<<16)));
}
// 关闭定时器
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:使用Systick中断来实现led的亮灭
LED引脚:PC0
***********************/
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_systick.h"
int main(void)
{
LED_GPIO_Config(); // 配置LED
while(1)
{
LED0(OFF);
SysTick_Delay_ms(1000); // 延时1s
LED0(ON);
SysTick_Delay_ms(1000); // 延时1s
}
}
USART-串口通信
串口通信协议
1.RS232标准
RS-232 标准的串口设备间常见的通讯结构图如下
在上面的通讯方式中,两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯。
2. 电平标准
根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准及 RS-232 标准
通讯标准 | 电平标准(发送端) |
---|---|
5V TTL | 逻辑 1: 2.4V-5V逻辑 0: 0~0.5V |
RS-232 | 逻辑 1: -15V~-3V逻辑 0: +3V~+15V |
常见的电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V 表示逻辑1, +15V 表示逻辑 0。使用 RS232 与 TTL 电平校准表示同一个信号时的对比 <br /><br />因为控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232电平的信号进行互相转换。
使用固件库操作USART
USART初始化结构体
typedef struct
{
uint32_t USART_BaudRate; //波特率 BRR
uint16_t USART_WordLength; //字长 CR1_M
uint16_t USART_StopBits; //停止位 CR2_STOP
uint16_t USART_Parity; //校验控制 CR1_PCE、CR1_PS
uint16_t USART_Mode; //模式选择 CR1_TE、CR1_RE
// 硬件流选择 CR3_CTSE、CR3_RTSE
uint16_t USART_HardwareFlowControl;
}USART_InitTypeDef;
同步时钟初始化结构体
typedef struct
{
uint16_t USART_Clock; // 同步时钟 CR2_CLKEN
uint16_t USART_CPOL; // 极性 CR2_CPOL
uint16_t USART_CPHA; // 相位 CR2_CPHA
uint16_t USART_LastBit; // 最后一个位的时钟脉冲 CR2_LBC
}USART_ClockInitTypeDef;
向串口发送和接收数据实例
bsp_usart.h
#ifndef __BSP_USART_H
#define __BSP_USART_H
#include "stm32f10x.h"
#include <stdio.h> // c语言的库函数
/*
* 串口宏定义,不同的串口挂载的总线和 IO 不一样,移植时需要修改这几个宏
*/
// 分别给5个串口定义5个宏,使用哪个串口时就打开哪个
#define DEBUG_USART1 0
#define DEBUG_USART2 0
#define DEBUG_USART3 0
#define DEBUG_USART4 0
#define DEBUG_USART5 1
#if DEBUG_USART1
// 串口1-USART1宏定义
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
#elif DEBUG_USART2
// 串口2-USART2宏定义
#define DEBUG_USARTx USART2
#define DEBUG_USART_CLK RCC_APB1Periph_USART2
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_2
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_3
#define DEBUG_USART_IRQ USART2_IRQn
#define DEBUG_USART_IRQHandler USART2_IRQHandler
#elif DEBUG_USART3
// 串口3-USART3宏定义
#define DEBUG_USARTx USART3
#define DEBUG_USART_CLK RCC_APB1Periph_USART3
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOB)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOB
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_RX_GPIO_PORT GPIOB
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11
#define DEBUG_USART_IRQ USART3_IRQn
#define DEBUG_USART_IRQHandler USART3_IRQHandler
#elif DEBUG_USART4
// 串口4-USART4宏定义
#define DEBUG_USARTx UART4
#define DEBUG_USART_CLK RCC_APB1Periph_UART4
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOC
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_RX_GPIO_PORT GPIOC
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11
#define DEBUG_USART_IRQ UART4_IRQn
#define DEBUG_USART_IRQHandler UART4_IRQHandler
#elif DEBUG_USART5
// 串口5-USART5宏定义
#define DEBUG_USARTx UART5
#define DEBUG_USART_CLK RCC_APB1Periph_UART5
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOC
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_12
#define DEBUG_USART_RX_GPIO_PORT GPIOD
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_2
#define DEBUG_USART_IRQ UART5_IRQn
#define DEBUG_USART_IRQHandler UART5_IRQHandler
#endif
void USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
void Usart_SendHalfWold(USART_TypeDef* pUSARTx, uint16_t data);
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num);
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str);
#endif // __BSP_USART_H
bsp_usart.c
#include "bsp_usart.h"
// 中断控制器NVIC配置
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置 USART 为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级为 1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置 NVIC */
NVIC_Init(&NVIC_InitStructure);
}
// USART初始化配置
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将 USART Tx 的 GPIO 配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将 USART Rx 的 GPIO 配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
// 给串口发送一个字节数据
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
// 发送数据
USART_SendData(pUSARTx, data);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 给串口发送两个字节数据
void Usart_SendHalfWold(USART_TypeDef* pUSARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
// 高8位
temp_h = (data & 0xff00) >> 8;
// 低8位
temp_l = data & 0xff;
/* 发送高8位数据 */
USART_SendData(pUSARTx, temp_h);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低8位数据 */
USART_SendData(pUSARTx, temp_l);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 发送8位数据的数组
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)
{
uint8_t i;
for(i=0; i<num; i++)
{
// 循环发送1个字节的数据
Usart_SendByte(pUSARTx, array[i]);
}
// 检测传输完成标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
// 发送字符串
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
uint8_t i=0;
do
{
Usart_SendByte(pUSARTx, *(str + i));
i++;
}while(*(str+i) != '\0'); // 当为字符串的结束字符时,则停止发送
// 检测传输完成标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
// 重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
// 发送一个字节数据到串口
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
// 等待发送完毕
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return ch;
}
// 重定向c库函数scanf到串口,重定向后可使用scan/getchar函数
int fgetc(FILE *f)
{
// 等待串口输入数据
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
// 将数据返回
return (int)USART_ReceiveData(DEBUG_USARTx);
}
/*-------------- 串口服务中断函数 当接收的外部发送的数据时,中断会进行响应--------------*/
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
{
// 临时变量,用来存储接收串口发来的数据
ucTemp = USART_ReceiveData(DEBUG_USARTx);
// 返回发送的数据
USART_SendData(DEBUG_USARTx, ucTemp);
}
}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:串口收发送数据
引脚:
***********************/
#include "stm32f10x.h"
#include "bsp_usart.h"
int main(void)
{
// 10个数据的数组
uint8_t data_arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 初始化USART
USART_Config();
// 向串口发送数据
// Usart_SendByte(DEBUG_USARTx, 0x64); // 发送一个字节的数据
// Usart_SendHalfWold(DEBUG_USARTx, 0x8889); // 发送2个字节的数据
// Usart_SendArray(DEBUG_USARTx, data_arr, 10); // 发送一个数组的数据
// Usart_SendStr(DEBUG_USARTx, "欢迎使用\n"); // 发送字符串
// printf("串口printf函数测试"); // 使用c库函数发送字符串 //相对应的是scanf()函数
// putchar('p'); // 使用c库函数发送一个字符 //相对于的是getchar()函数
while(1)
{
}
}
使用串口来控制LED灯
bsp_led.h
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f10x.h"
#define LED_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_Pin_0
#define LED1_GPIO_PIN GPIO_Pin_1
#define LED5_GPIO_PIN GPIO_Pin_5
#define OFF 0
#define ON 1
#define LED0_TOGGLE (LED_GPIO_PORT->ODR ^= LED0_GPIO_PIN) // 改变odr输出的状态
#define LED1_TOGGLE (LED_GPIO_PORT->ODR ^= LED1_GPIO_PIN) // 改变odr输出的状态
#define LED2_TOGGLE (LED_GPIO_PORT->ODR ^= LED5_GPIO_PIN) // 改变odr输出的状态
#define LED0(a) if(a) GPIO_ResetBits(LED_GPIO_PORT, LED0_GPIO_PIN); \
else GPIO_SetBits(LED_GPIO_PORT, LED0_GPIO_PIN);
#define LED1(a) if(a) GPIO_ResetBits(LED_GPIO_PORT, LED1_GPIO_PIN); \
else GPIO_SetBits(LED_GPIO_PORT, LED1_GPIO_PIN);
#define LED2(a) if(a) GPIO_ResetBits(LED_GPIO_PORT, LED5_GPIO_PIN); \
else GPIO_SetBits(LED_GPIO_PORT, LED5_GPIO_PIN);
void LED_GPIO_Config(void);
#endif //__BSP_LED_H
bsp_led.c
// bsp: board support package 板级支持包
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
// 初始化结构体
GPIO_InitTypeDef GPIO_InitStruct;
//LED_GPIO_CLK_ENABLE;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 初始化
GPIO_InitStruct.GPIO_Pin |= LED0_GPIO_PIN;
GPIO_InitStruct.GPIO_Pin |= LED1_GPIO_PIN;
GPIO_InitStruct.GPIO_Pin |= LED5_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
bsp_usart.h
// 与使用向串口发送和接收数据实例的 bsp_usart.h头文件一样
bsp_usart.c
#include "bsp_usart.h"
/*
* 要取消中断服务,不然会影响收的数据
*/
//// 中断控制器NVIC配置
//static void NVIC_Configuration(void)
//{
//
// NVIC_InitTypeDef NVIC_InitStructure;
// /* 嵌套向量中断控制器组选择 */
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// /* 配置 USART 为中断源 */
// NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
//
// /* 抢断优先级为 1 */
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//
// /* 子优先级为 1 */
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//
// /* 使能中断 */
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//
// /* 初始化配置 NVIC */
// NVIC_Init(&NVIC_InitStructure);
//}
// USART初始化配置
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将 USART Tx 的 GPIO 配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将 USART Rx 的 GPIO 配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
/*
* 要取消中断服务,不然会影响收的数据
*/
// // 串口中断优先级配置
// NVIC_Configuration();
// // 使能串口接收中断
// USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
// 给串口发送数据
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
// 发送数据
USART_SendData(pUSARTx, data);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 给串口发送数据
void Usart_SendHalfWold(USART_TypeDef* pUSARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
// 高8位
temp_h = (data & 0xff00) >> 8;
// 低8位
temp_l = data & 0xff;
/* 发送高8位数据 */
USART_SendData(pUSARTx, temp_h);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低8位数据 */
USART_SendData(pUSARTx, temp_l);
// 检测传输数据寄存器空标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 发送8位数据的数组
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)
{
uint8_t i;
for(i=0; i<num; i++)
{
// 循环发送1个字节的数据
Usart_SendByte(pUSARTx, array[i]);
}
// 检测传输完成标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
// 发送字符串
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
uint8_t i=0;
do
{
Usart_SendByte(pUSARTx, *(str + i));
i++;
}while(*(str+i) != '\0'); // 当为字符串的结束字符时,则停止发送
// 检测传输完成标志
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
// 重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
// 发送一个字节数据到串口
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
// 等待发送完毕
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return ch;
}
// 重定向c库函数scanf到串口,重定向后可使用scan/getchar函数
int fgetc(FILE *f)
{
// 等待串口输入数据
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
// 将数据返回
return (int)USART_ReceiveData(DEBUG_USARTx);
}
/*
* 要取消中断服务,不然会影响收的数据
*/
//// 串口服务中断
//void DEBUG_USART_IRQHandler(void)
//{
// uint8_t ucTemp;
// if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
// {
// // 临时变量,用来存储接收串口发来的数据
// ucTemp = USART_ReceiveData(DEBUG_USARTx);
//
// // 返回发送的数据
// USART_SendData(DEBUG_USARTx, ucTemp);
// }
//}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:使用串口给单片机发数据从而改变三个led灯的状态
引脚:
***********************/
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_led.h"
int main(void)
{
uint8_t ch; // 用于存储串口发送来的数据
// 初始化USART
USART_Config();
// 初始化LED
LED_GPIO_Config();
while(1)
{
ch = getchar(); // 获取返回来的数据
printf("ch=%c\n", ch);
switch(ch)
{
case '1': LED0_TOGGLE; // 控制LED0的状态
break;
case '2': LED1_TOGGLE; // 控制LED1的状态
break;
case '3': LED2_TOGGLE; // 控制LED2的状态
break;
}
}
}
DMA直接存储器存取
DMA_InitTypeDef初始化结构体解析
typedef struct
{
/*------------------ 数据从哪里来,要到哪里去 ------------------------------- */
uint32_t DMA_PeripheralBaseAddr; // 外设地址 DMA_CPAR
uint32_t DMA_MemoryBaseAddr; // 存储器地址 DMA_CMAR
uint32_t DMA_DIR; // 传输方向 DMA_CCR:DIR
/*------------------ 数据要传多少,传的单位是什么 ---------------------------- */
uint32_t DMA_BufferSize; // 传输数目 DMA_CNDTR
uint32_t DMA_PeripheralInc; // 外设地址增量模式 DMA_CCRx:PINC
uint32_t DMA_MemoryInc; // 存储器地址增量模式 DMA_CCRx:MINC
uint32_t DMA_PeripheralDataSize; // 外设数据宽度 DMA_CCRx:PSIZE
uint32_t DMA_MemoryDataSize; // 存储器数据宽度 DMA_CCRx:MSIZE
/*------------------ 什么时候传输结束 ---------------------------- */
uint32_t DMA_Mode; // 模式选择 DMA_CCRx:CIRC
uint32_t DMA_Priority; // 通道优先级
uint32_t DMA_M2M; // 存储器到存储器模式
}DMA_InitTypeDef;
DMA存储器到存储器传输实例
bsp_dma.h
#ifndef __BSP_DMA_H
#define __BSP_DMA_H
#include "stm32f10x.h"
// 要发送的数据大小
#define BUFFER_SIZE 32
#define MTM_DMA_CLK RCC_AHBPeriph_DMA1 // 选择DMA时钟
#define MTM_DMA_CHANNEL DMA1_Channel6 // 选择6通道
#define MTM_DMA_FLAG_TC DMA1_FLAG_TC6 // DMA1通道6传输完成标志
void MtoM_DMA_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer,
uint32_t* pBuffer1, uint16_t BufferLength);
#endif // __BSP_DMA_H
bsp_dma.c
#include "bsp_dma.h"
/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
* const关键字将aSRC_Const_Buffer数组变量定义为常量类型
* 表示数据存储在内部的FLASH中
*/
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
/* 定义DMA传输目标存储器
* 存储在内部的SRAM中
*/
uint32_t aDST_Buffer[BUFFER_SIZE];
void MtoM_DMA_Config(void)
{
// 初始化结构体
DMA_InitTypeDef DMA_InitStruct;
// 打开时钟
RCC_AHBPeriphClockCmd(MTM_DMA_CLK, ENABLE);
//
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer; // 外设地址
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer; // 存储器地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 传输方向
DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE; // 传输数目
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable; // 外设地址增量模式
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 外设数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 存储器地址增量模式
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 外设数据宽度
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // 模式选择
DMA_InitStruct.DMA_Priority = DMA_Priority_High; // 通道优先级
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable; // 存储器到存储器模式
// 初始化
DMA_Init(MTM_DMA_CHANNEL, &DMA_InitStruct);
// 标志触发器清零
DMA_ClearFlag(MTM_DMA_FLAG_TC);
// 使能DMA
DMA_Cmd(MTM_DMA_CHANNEL, ENABLE);
}
/**
* 判断指定长度的两个数据源是否完全相等,
* 如果完全相等返回1,只要其中一对数据不相等返回0
*/
uint8_t Buffercmp(const uint32_t* pBuffer,
uint32_t* pBuffer1, uint16_t BufferLength)
{
/* 数据长度递减 */
while(BufferLength--)
{
/* 判断两个数据源是否对应相等 */
if(*pBuffer != *pBuffer1)
{
/* 对应数据源不相等马上退出函数,并返回0 */
return 0;
}
/* 递增两个数据源的地址指针 */
pBuffer++;
pBuffer1++;
}
/* 完成判断并且对应数据相对 */
return 1;
}
bsp_led.h
// bsp_led.h和bsp_led.c文件参照之前的
main.c
/**********************
芯片:STM32F103C8T6
实现功能:存储器到存储器传输实例 传输成功后亮成功的led灯,失败亮失败的led灯
引脚:
***********************/
#include "stm32f10x.h"
#include "bsp_dma.h"
#include "bsp_led.h"
// 声明两个外部文件来的变量 extern表示导入外部的变量(非头文件里的)
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
int main(void)
{
uint8_t status = 0;
// 初始化LED
LED_GPIO_Config();
// 将3个led都熄灭,因为程序初始化后led灯默认都点亮的
LED0(OFF);
LED1(OFF);
LED2(OFF);
// 初始化MtoM_DMA_Config
MtoM_DMA_Config();
// 检测传输是否完成
while(DMA_GetFlagStatus(MTM_DMA_FLAG_TC) == RESET);
// 比较函数,比较目标的数据和传输后存储的数据是否相同
status = Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
if(0 == status)
{
LED0(ON); // 失败亮led0
}
else
{
LED1(ON); // 成功亮led1
}
while(1)
{
}
}
使用串口进行DMA传输(M To P)
bsp_dma_mtp.h
#ifndef __BSP_DMA_MTP_H
#define __BSP_DMA_MTP_H
#include "stm32f10x.h"
#include <stdio.h>
// 串口工作参数宏定义
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define USART_TX_DMA_CLK RCC_AHBPeriph_DMA1
#define USART_TX_DMA_CHANNEL DMA1_Channel4 // 选择第4通道
#define USART_TX_DMA_FLAG_TC DMA1_FLAG_TC4
#define USART_DR_ADDRESS (USART1_BASE+0x04) // 外设寄存地址
#define SENDBUFF_SIZE 5000
void USARTx_DMA_Config(void);
void USART_Config(void);
#endif /* __BSP_DMA_MTP_H */
bsp_dma_mtp.c
#include "bsp_dma_mtp.h"
// 定义存储变量
uint8_t SendBuff[SENDBUFF_SIZE];
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
// 重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
// Memory -> P (USART->DR)
void USARTx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(USART_TX_DMA_CLK, ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)SendBuff;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_BufferSize = SENDBUFF_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // 表示传完一次就结束,DMA_Mode_Circular表示传完继续循环传
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStruct);
DMA_ClearFlag(USART_TX_DMA_FLAG_TC);
DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);
}
main.c
/**********************
芯片:STM32F103C8T6
实现功能:使用串口进行DMA传输(M To P)
引脚:
***********************/
#include "stm32f10x.h"
#include "bsp_dma_mtp.h"
#include "bsp_led.h"
extern uint8_t SendBuff[SENDBUFF_SIZE];
// 软件延时
#define SOFT_DELAY Delay(0x0FFFFF);
void Delay(__IO u32 nCount);
int main(void)
{
uint16_t i=0;
// 初始化LED
LED_GPIO_Config();
// 初始化串口
USART_Config();
// 将3个led都熄灭,因为程序初始化后led灯默认都点亮的
LED0(OFF);
LED1(OFF);
LED2(OFF);
// 初始化SendBuff内的值
for(i=0; i<SENDBUFF_SIZE; i++)
{
SendBuff[i] = 'P';
}
// 初始化DMA
USARTx_DMA_Config();
// DMA使能
USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
while(1)
{
// 用于观察当串口向DMA写数据时,观察DMA是否是异步运行的
LED0_TOGGLE;
Delay(0xFFFFF);
}
}
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}