自定义简单的固件库
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的低四位暂存在currentmodecurrentmode = ((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~Pin7if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00){// 先备份CRL寄存器的值tmpreg = GPIOx->CRL;// 循环,从Pin0开始配对,找出具体的Pinfor (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可对引脚置0GPIOx->BRR = (((uint32_t)0x01) << pinpos);}else{// 判断是否为上拉输入模式if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU){// 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1GPIOx->BSRR = (((uint32_t)0x01) << pinpos);}}}}// 把前面处理后的暂存值写入到CRL寄存器之中GPIOx->CRL = tmpreg;}/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/// 配置端口高8位,即Pin8~Pin15if (GPIO_InitStruct->GPIO_Pin > 0x00FF){// // 先备份CRH寄存器的值tmpreg = GPIOx->CRH;// 循环,从Pin8开始配对,找出具体的Pinfor (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可对引脚置0GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));}// 判断是否为上拉输入模式if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU){// 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1GPIOx->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 0void 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){// KEY1GPIO_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);// KEY2GPIO_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引脚:PC13led0引脚:PB0led1引脚: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(); // 配置LEDKEY_GPIO_Config(); // 配置KEYwhile(1){// 按键1控制LED0if(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,弹开时0count++;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 3static 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 inputif(key_data == 0)key_state = KEY_CONFIRM_INPUT;break;case KEY_CONFIRM_INPUT://confirm key inputif(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寄存器复位成默认状态// 使能HSERCC_HSEConfig(RCC_HSE_ON);// 等待HSE使能成功,成功返回1,错误返回0HSEStatus = RCC_WaitForHSEStartUp();if(HSEStatus == SUCCESS){// 启动成功// 使能预取指FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);// 设置flase等待周期 设置两个周期等待FLASH_SetLatency(FLASH_Latency_2);// 配置三个总线的分屏因子RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLKRCC_PCLK1Config(RCC_SYSCLK_Div2); // PCLK2RCC_PCLK2Config(RCC_SYSCLK_Div1); // PCLK1// 配置锁相环RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x); // 配置PLLCLK = HSE * RCC_PLLMul_xRCC_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寄存器复位成默认状态// 使能HSERCC_HSICmd(ENABLE);// 等待HSI使能成功,成功返回1,错误返回0HSIStatus = RCC->CR & RCC_CR_HSIRDY;if(HSIStatus == SUCCESS){// 启动成功// 使能预取指FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);// 设置flase等待周期 设置两个周期等待FLASH_SetLatency(FLASH_Latency_2);// 配置三个总线的分屏因子 HCLK = PCLK2 PCLK1/2 = 72MRCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK 72MRCC_PCLK1Config(RCC_SYSCLK_Div2); // PCLK1 低速36MRCC_PCLK2Config(RCC_SYSCLK_Div1); // PCLK2 高速72M// 配置锁相环RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x); // 配置PLLCLK = HSI * RCC_PLLMul_xRCC_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_GPIOAvoid 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); // 组1NVIC_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的GPIORCC_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; // 线为0EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 模式配置成中断模式EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发EXTI_InitStruct.EXTI_LineCmd = ENABLE; // 使能中断屏蔽寄存器设为1EXTI_Init(&EXTI_InitStruct); // 将设置的值进行配置// 编写中断服务函数// 中断函数都在stm32f10x_it 该中断为 EXTI0_IRQHandler}
stm32f10x_it.c
#include "bsp_led.h"#include "bsp_exti.h"// EXTI0中断函数void EXTI0_IRQHandler(void){// 判断中断标志位是否为1if(EXTI_GetITStatus(EXTI_Line0) != RESET){LED0_TOGGLE;}// 清除中断标志位EXTI_ClearITPendingBit(EXTI_Line0);}
main.c
/**********************芯片:STM32F103C8T6实现功能:使用EXTI来实现按键控制led亮-灭KEY引脚:PA0LED引脚: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(); // 配置LEDwhile(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"// 定时1usvoid SysTick_Delay_us(uint32_t us){// 初始化SysTick_Config(72); // 配置1us的时间for(uint16_t i=0; i<us; i++){// 当计数器减到0时,则控制及状态寄存器的第16位为1即COUNTFLAG=1while(!((SysTick->CTRL) & (1<<16)));}// 关闭定时器SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;}// 定时1msvoid 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(); // 配置LEDwhile(1){LED0(OFF);SysTick_Delay_ms(1000); // 延时1sLED0(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; //波特率 BRRuint16_t USART_WordLength; //字长 CR1_Muint16_t USART_StopBits; //停止位 CR2_STOPuint16_t USART_Parity; //校验控制 CR1_PCE、CR1_PSuint16_t USART_Mode; //模式选择 CR1_TE、CR1_RE// 硬件流选择 CR3_CTSE、CR3_RTSEuint16_t USART_HardwareFlowControl;}USART_InitTypeDef;
同步时钟初始化结构体
typedef struct{uint16_t USART_Clock; // 同步时钟 CR2_CLKENuint16_t USART_CPOL; // 极性 CR2_CPOLuint16_t USART_CPHA; // 相位 CR2_CPHAuint16_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#endifvoid 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};// 初始化USARTUSART_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; // 用于存储串口发送来的数据// 初始化USARTUSART_Config();// 初始化LEDLED_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_CPARuint32_t DMA_MemoryBaseAddr; // 存储器地址 DMA_CMARuint32_t DMA_DIR; // 传输方向 DMA_CCR:DIR/*------------------ 数据要传多少,传的单位是什么 ---------------------------- */uint32_t DMA_BufferSize; // 传输数目 DMA_CNDTRuint32_t DMA_PeripheralInc; // 外设地址增量模式 DMA_CCRx:PINCuint32_t DMA_MemoryInc; // 存储器地址增量模式 DMA_CCRx:MINCuint32_t DMA_PeripheralDataSize; // 外设数据宽度 DMA_CCRx:PSIZEuint32_t DMA_MemoryDataSize; // 存储器数据宽度 DMA_CCRx:MSIZE/*------------------ 什么时候传输结束 ---------------------------- */uint32_t DMA_Mode; // 模式选择 DMA_CCRx:CIRCuint32_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);// 使能DMADMA_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;// 初始化LEDLED_GPIO_Config();// 将3个led都熄灭,因为程序初始化后led灯默认都点亮的LED0(OFF);LED1(OFF);LED2(OFF);// 初始化MtoM_DMA_ConfigMtoM_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 5000void 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;// 初始化LEDLED_GPIO_Config();// 初始化串口USART_Config();// 将3个led都熄灭,因为程序初始化后led灯默认都点亮的LED0(OFF);LED1(OFF);LED2(OFF);// 初始化SendBuff内的值for(i=0; i<SENDBUFF_SIZE; i++){SendBuff[i] = 'P';}// 初始化DMAUSARTx_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--);}

