网站策划与设计,如何注册网站卖东西,建设网站应该注意的地方,新乐企业网站建设本节我们将会对STM32F103的硬件资源GPIO和串口进行介绍。 一、GPIO 1.1 电路原理图 LED电路原理图如下图所示#xff1a; 其中#xff1a; LED1连接到PA8引脚#xff0c;低电平点亮#xff1b;LED2连接到PD2引脚#xff0c;低电平点亮#xff1b; 1.2 GPIO引脚介绍 STM32… 本节我们将会对STM32F103的硬件资源GPIO和串口进行介绍。 一、GPIO 1.1 电路原理图 LED电路原理图如下图所示 其中 LED1连接到PA8引脚低电平点亮LED2连接到PD2引脚低电平点亮 1.2 GPIO引脚介绍 STM32F103系列共有7组GPIO分别为 GPIOA, GPIOB, GPIOC, GPIOD、 GPIOE、GPIOF、GPIOG。其中GPIOA、GPIOB、GPIOC、GPIOD是常用的GPIOE~GPIOG只有一部分引脚在某些型号的STM32F103系列中有效。 每组GPIO通常包含16个引脚。但并非所有引脚都在所有STM32F103型号中都有效具体取决于具体芯片封装。 GPIOA16 个引脚(PA0 - PA15)GPIOB16 个引脚(PB0 - PB15)GPIOC16 个引脚(PC0 - PC15)GPIOD16 个引脚(PD0 - PD15)GPIOE16个引脚(PE0 - PE15)GPIOF16个引脚(PF0 - PF15)GPIOG16个引脚(PG0 - PG15)。 1.3 GPIO寄存器 在STM32F103中GPIO引脚的配置和控制是通过寄存器来实现的。每个GPIO端口(如GPIOA、GPIOB 等)都有一组寄存器来控制引脚的方向、输出类型、上拉/下拉电阻、输入模式等。 两个32位配置寄存器GPIOx_CRH、GPIOx_CRL;两个32位数据寄存器GPIOx_IDR、GPIOx_ODR一个32位设置/清除寄存器GPIOx_BSRR一个16位复位寄存器GPIOx_BRR一个32位锁定寄存器GPIOx_LCKR 1.3.1 端口配置低寄存器(GPIOx_CRL) GPIOx_CRL端口配置低8位寄存器(xA~E)四位控制1个引脚共控制8个引脚(0~7引脚) 31:2827:2423:2019:1615:1211:87:43:0CNF7[1:0]MODE7[1:0]........................CNF0[1:0]MODE0[1:0] 其中 MODE[1:0]CNF[1:0]输入模式0 00 0模拟输入模式0 1浮空输入模式1 0上拉/下卡输入模式1 1保留输出模式0 1最大速度10MHz1 0最大速度2MHz1 1最大速度50MHz0 0通用推挽输出模式0 1通用开漏输出模式1 0复用功能推挽输出模式1 1复用功能开漏输出模式 1.3.2 端口配置高寄存器(GPIOx_CRH) GPIOx_CRH四位控制1个引脚(xA~E)共控制8个引脚(8~15引脚)同上不再重复介绍。 1.3.3 端口输入数据寄存器(GPIOx_IDR) GPIOx_IDR端口输入数据寄存器(xA~E) 31~161514131211109876543210保留IDR15............................IDR0 31~16位始终读0低16位为只读并且只能以字(16位)的形式读出。 1.3.4 端口输出数据寄存器(GPIOx_ODR) GPIOx_ODR端口输出数据寄存器(xA~E) 31~161514131211109876543210保留ODR15............................ODR0 31~16位始终读0低16位可读可写并且只能以字(16位)的形式操作。 1.3.5 端口设置/清除寄存器(GPIOx_BSRR) GPIOx_BSRR端口设置/清除寄存器(xA~E) 31~1615~0BR15~BR0BS15~BS0清除端口x的位y这些位只能以字(16位)的形式操作。0对对应的ODRy位不产生影响1清除对应的ODRy位为0设置端口x的位y这些位只能以字(16位)的形式操作。0对对应的ODRy位不产生影响1设置对应的ODRy位为1 1.3.6 端口清除寄存器(GPIOx_BRR) GPIOx_BRR端口清除寄存器(xA~E) 31~1615~0保留BR15~BR0清除端口x的位y这些位只能以字(16位)的形式操作。0对对应的ODRy位不产生影响1清除对应的ODRy位为1 1.4 GPIO输出输出编码流程 GPIO的输入/输出一般步骤如下 (1)使能外设时钟RCC-APB2ENR 在设置STM32外设的时候任何时候都需要先使能该外设的始终APB2ENR是APB2(高速外设)总线上的外设时钟使能寄存器 15141312111098ADC3 ENUART1 ENTIM8 ENSPI1 ENTIM1 ENADC2 ENADC1 ENIOPG76543210IOPFIOPEIOPDIOPCIOPBIOPA保留AFIO EN 其中对应位置1表示使能。如果使能I/O复用需要设置AFIO EN位为1。 (2)配置输入/输出GPIOx-CRL、GPIOx-CRH (3)输入GPIOx-IDR、输出GPIOx-ODR。 1.5 代码实现 1.5.1 GPIO初始化函数 /******************************************************** ********************
* File Name : gpio.c
* Author : Zhengyang
* Description : 初始化GPIO口portx端口号PORTA~Epinx:引脚号PORT0~15gpio_cfg:输入输出模式配置bit输出电平设置 高HIGH 低LOW
********************************************************************************/
void gpio_init(PORTx_PINx portx_pinx,GPIO_CFG gpio_cfg,BIT_ACTION bit)
{GPIO_TypeDef *GPIO;u8 portx portx_pinx/16; //获取端口号u8 pinx portx_pinx%16; //获取引脚号if(portx0)GPIOGPIOA;else if(portx1)GPIOGPIOB;else if(portx2)GPIOGPIOC;else if(portx3)GPIOGPIOD;elseGPIOGPIOE;RCC-APB2ENR|1(portx2); //外设时钟使能if(pinx8){GPIO-CRL ~(0x0F(pinx*4)); //该引脚模式配置四位清零GPIO-CRL|gpio_cfg(4*pinx); //端口配置寄存器CRL}else if(pinx8) {GPIO-CRH ~(0x0F((pinx-8)*4)); //该引脚模式配置四位清零GPIO-CRH|gpio_cfg(4*(pinx-8)); //端口配置寄存器CRH}if((gpio_cfg0x03)!0x00) //输出{if(pinx8)GPIO-ODR|bitpinx; //端口数据输出寄存器ODRelse GPIO-ODR|bitpinx; //端口数据输出寄存器 }
} 这里PORTx_PINx、GPIO_CFG、BIT_ACTION均是枚举类型 /************************************* 端口配置 ************************************************************/
typedef enum //宏定义端口配置
{//这里的值不能改GPI 0x00, //定义管脚输入方向 GPO_SpeedMax_10 0x01, //定义管脚输出方向 最大速度10MHZGPO_SpeedMax_2 0x02, //定义管脚输出方向 最大速度2MHZGPO_SpeedMax_50 0x03, //定义管脚输出方向 最大速度50MHZ//输入模式可用GPI_DOWN 0x08|GPI, //输入下拉 PxODR需配置为0 GPI_UP 0x08|GPI, //输入上拉 PxODR需配置为1 复用功能时一般采用GPI_ANALOG 0x00|GPI, //模拟输入GPI_FLOAT 0x04|GPI , //浮空输入//输出模式不能用GPO_PUSH_PULL 0x00, //通用推挽输出GPO_OPEN_DRAIN 0x04, //通用开漏输出GPO_MULPUSH_PULL 0x08, //复用推挽输出GPO_MULOPEN_DRAIN 0x0C, //复用开漏输出//输出模式可用GPO_PUSH_PULL_50 GPO_SpeedMax_50| GPO_PUSH_PULL, //通用推挽输出最大速度50MHZ 通用输出一般设置GPO_PUSH_PULL_10 GPO_SpeedMax_10| GPO_PUSH_PULL, //通用推挽输出最大速度10MHZ GPO_PUSH_PULL_2 GPO_SpeedMax_2 | GPO_PUSH_PULL, //通用推挽输出最大速度2MHZGPO_MULPUSH_PULL_2 GPO_SpeedMax_2| GPO_MULPUSH_PULL, //复用推挽输出最大速度2MHZGPO_MULPUSH_PULL_10 GPO_SpeedMax_10| GPO_MULPUSH_PULL, //复用推挽输出最大速度10MHZGPO_MULPUSH_PULL_50 GPO_SpeedMax_50| GPO_MULPUSH_PULL //复用推挽输出最大速度50MHZ 复用功能 输出模式一般采用这样设置
}GPIO_CFG;/***************************************** 宏定义引脚号 *************************************************************/
typedef enum
{PA00,PA11,PA22,PA33,PA44,PA55,......PE1377,PE1478,PE1579} PORTx_PINx;typedef enum //外部端口输出电平
{ LOW 0,HIGH 1
} BIT_ACTION; 1.5.2 PXout/PXIn 定义GPIO输入输出宏 /********************** 位运算符优先级低于算术运算符 | ~ 所以位运算符必须加括号 ***********************/
#define Bit_Band(Addr,Bit_Num) *((volatile unsigned long *)((Addr0xF0000000)0x02000000((Addr0xfffff)5)(Bit_Num2)))/********************************************** I/O 地址映射 **************************************************/
#define GPIOA_IDR (GPIOA_BASE 0x08) //0x40010808
#define GPIOA_ODR (GPIOA_BASE 0x0C) //0x4001080C
#define GPIOB_IDR (GPIOB_BASE 0x08) //0x40010C08
#define GPIOB_ODR (GPIOB_BASE 0x0C) //0x40010C0C
#define GPIOC_IDR (GPIOC_BASE 0x08) //0x40011008
#define GPIOC_ODR (GPIOC_BASE 0x0C) //0x4001100C
#define GPIOD_IDR (GPIOD_BASE 0x08) //0x40011408
#define GPIOD_ODR (GPIOD_BASE 0x0C) //0x4001180C
#define GPIOE_IDR (GPIOE_BASE 0x08) //0x40011808
#define GPIOE_ODR (GPIOE_BASE 0x0C) //0x4001180C/********************************************** 宏定义输入输出 ************************************************/
#define PAout(Bit_Num) Bit_Band(GPIOA_ODR,Bit_Num) //输出 1位输出
#define PAin(Bit_Num) Bit_Band(GPIOA_IDR,Bit_Num) //输入
#define PBout(Bit_Num) Bit_Band(GPIOB_ODR,Bit_Num)
#define PBin(Bit_Num) Bit_Band(GPIOB_IDR,Bit_Num)
#define PCout(Bit_Num) Bit_Band(GPIOC_ODR,Bit_Num)
#define PCin(Bit_Num) Bit_Band(GPIOC_IDR,Bit_Num)
#define PDout(Bit_Num) Bit_Band(GPIOD_ODR,Bit_Num)
#define PDin(Bit_Num) Bit_Band(GPIOD_IDR,Bit_Num)
#define PEout(Bit_Num) Bit_Band(GPIOE_ODR,Bit_Num)
#define PEout(Bit_Num) Bit_Band(GPIOE_ODR,Bit_Num) 这里利用到了STM32的位段功能Cortex-M3储存器包含两个位段(bit-band)区将别名寄存器的每个字映射到位段寄存器的一个位。 映射公式(外设寄存器和SRAM可被映射) \[bit\_word\_addr bit\_band\_base (byte\_offset \times 32) (bit\_number * 4) \] 其中 bit_word_addr别名寄存器中的地址映射到某一个目标位bit_band_base别名区的起始地址byte_offset目标位的字节在位段的序号bit_number目标位所在位置(0~31) 例如SRAM中某一变量地址为0x20000300字节中的位2 \[0\times22006008 0\times22000000 ((0\times20000300 - 0\times20000000) \times 8 2) \times 4% \] 1.5.3 点亮LED 如需点亮LED我们只需要在main函数添加如下代码 gpio_init(PD2,GPO_SpeedMax_50,HIGH); // PD2接入LED2
gpio_init(PA8,GPO_SpeedMax_50,HIGH); // PA8接入LED1
while(1)
{ PAout(8) 0;PDout(2) 1;delay_ms(1500);PAout(8) 1;PDout(2) 0;delay_ms(1500);
} 1.5.4 烧录测试 编译完成后通过ISP烧录到开发版我们可以看到LED1、LED2交替点亮时间间隔为1.5s。 二、串口 STM32F103微控制器提供了5组串口分别是USART1, USART2, 和USART3、UART4、UART5 其中1-3是通用同步/异步串行接口USART(Universal Synchronous/Asynchronous Receiver/Transmitter)4,、5是通用异步串行接口UART(Universal Asynchronous Receiver/Transmitter)。 这些串口可以用来进行UART 通信例如发送和接收数据。 2.1 串口引脚介绍 串口TXRXUSART_1PA9PA10USART_2PA2PA3USART_3PB10PB11UART_4PC10PC11UART_5PC12PD2 2.2 串口寄存器 2.2.1 波特率设置寄存器(UARTx_BRR) 波特率计算公式如下$$波特率\frac{f_{pclkx}}{ 16 \times USARTDIV}$$ 其中 PCLK1用于USART2、USART3、USART4、USART5最大36MHzPCLK2用于USART1最大72MHz。 例如PCLK272MHz设置波特率为9600 USARTDIV 72000000 / (9600 * 16) 468.75 DIV_Fraction 16 * 0.15 12 0x0C DIV_Mantissa 468.75 0x1D4 。 2.2.2 控制寄存器1(UARTx_CR1) 2.2.3 控制寄存器2(UARTx_CR2) 2.2.4 控制寄存器3(UARTx_CR3) 2.2.5 数据寄存器(UARTx_DR) USART_DR实际是包含了两个寄存器一个专门用于发送的TDR一个专门用于接收的RDR 进行发送数据操作时往USART_DR写入数据会自动存储在TDR内当进行读取数据操作时向USART_DR读取数据会自动提取RDR数据。 串行通信时一位一位传输的所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的发送数据时把TDR内容转移到发送移位寄存器上接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。 2.2.6 状态寄存器(UARTx_SR) 状态寄存器适用于检测串口此时所处的状态。 它能够检测到的状态有发送寄存器空位、发送完成位、读数据寄存器非空位、检测到主线空闲位、过载错误为等等。 主要关注两个位RXNE和TC(第5、6两位) RXNE(读数据寄存器非空)当该位被置1的时候就是提示已经有数据被接收到了并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)这时候要做的就是尽快读取USART_DR从而将该位清零也可以向该位写0直接清除TC(发送完成)当该位被置1的时候表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断则会产生中断。该位也有两种清零方式读USART_SR写USART_DR直接向该位写0。 2.3 串口发送/接收编码流程 (1)串口时钟使能RCC-APB2ENR 在设置STM32外设的时候任何时候都需要先使能该外设的始终APB2ENR是APB2(高速外设)总线上的外设时钟使能寄存器 15141312111098ADC3 ENUART1 ENTIM8 ENSPI1 ENTIM1 ENADC2 ENADC1 ENIOPG76543210IOPFIOPEIOPDIOPCIOPBIOPA保留AFIO EN USART1是挂在APB2如果使用串口1需要使能位14。 (2)串口复位/停止复位RCC-APB2RSTR 一般在系统刚开始配置外设时都会先复位该外设的操作 15141312111098ADC3 RSTUART1 RSTTIM8 RSTSPI1 RSTTIM1 RSTADC2 RSTADC1 RSTIOPG RST76543210IOPF RSTIOPE RSTIOPD RSTIOPC RSTIOPB RSTIOPA RST保留AFIO RST 写1复位使其重新工作写0结束复位。 (3)使能串口时钟以及I/O状态设置 RX下拉输入TX复用推挽输出模式 (4)设置USART_CR1的PEIE和RXNEIE PEIE校验中断使能 校验错误硬件PE位置1RXNEIE使能接收中断 (5)串口参数设置 计算波特率利用USART_BRR寄存器选择要求的波特率设置USART_CR1的M位来定义字长设置USART_CR2中STOP位来定义停止位的位数 (6)设置USART_CR1中的TE位发送使能 (7)设置USART_CR1中的RE位接收使能 (8)设置USART_CR1中的UE位来使能串口 (9)把要发送的数据写进USART_DR寄存器(此动作清除TXE)位在只有一个缓存器的情况下对每个待发送的数据重复操作 (10)在USART_DR寄存器中写入最后一个数据字后要等待UART_SR寄存器TC1它表示最后一个数据帧的传输结束当需要关闭USART或需要进入停机模式前。需要确认传输结束避免破坏最后一次传输。 2.4 代码实现 2.4.1 串口初始化 /**************************************************************************************************************** Description: 初始化串口设置波特率* parameter : USARTn_e 模块号(USART_1~UART_5)baud 波特率如9600、19200、56000、115200等pclk pclk2时钟用于USART1 等于系统时钟外部晶振*9 单位MHX 一般为72pclk1时钟用于USART2~5 等于系统时钟外部晶振*9 单位MHX 一般为36 * Sample usage: usart_init (USART_3, 9600); //初始化串口3波特率为9600* USARTx-BRR(32) 波特率设置寄存器 15~4:USART分频器除法因子(USARTDIV)的整数部分 3~0 :USART分频器除法因子(USARTDIV)的小数部分波特率f(plckx)/(16*USARTDIV) **************************************************************************************************************/
void usart_init (USARTn usartn, u32 baud)
{u8 pclk;float temp;u16 mantissa; //USARTDIV的整数部分u16 fraction; //USARTDIV小数部分if(usartn1) //串口时钟选择pclkPCLK2;elsepclkPCLK1;temp (float)(pclk*1000000)/(baud*16);mantissa temp; //得到整数部分fraction(temp-mantissa)*16; //得到小数部分mantissa4;mantissafraction; //USART分频器除法因子(USARTDIV)/* 配置 UART功能的 复用管脚 */switch(usartn){
//***********************************************************USART1***********************************************case USART_1:RCC-APB2ENR | 114; //使能USART1时钟RCC-APB2RSTR | 114; //复位USART1RCC-APB2RSTR ~(114); //停止复位 if(USART1_TX PA9){gpio_init(PA9,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZ }else if(USART1_TX PB6){RCC-APB2ENR | 10; //使能I/O复用时钟gpio_init(PB6,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZAFIO-MAPR | 12; //USART1重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}if(USART1_RX PA10){gpio_init(PA10, GPI_DOWN,HIGH); //输入下拉PAout(10)0; //PxODR需配置为0 }else if(USART1_RX PB7){gpio_init(PB7,GPI_DOWN,HIGH); //输入下拉PBout(7)0; AFIO-MAPR | 12; //USART1重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}#if EN_USART1_RX USARTx[usartn]-CR1|18; //位8 PEIE 校验中断使能 校验错误硬件PE位置1USARTx[usartn]-CR1|15; //位5: RXNEIE 使能接收中断
#endif#if EN_USART1_TX USARTx[usartn]-CR1|16; //位6TCIE 发送完成中断使能位 当SR中的TC硬件置1将产生中断
#endif break; //*****************************************************USART2********************************************************case USART_2:RCC-APB1ENR | 117; //使能USART2时钟RCC-APB1RSTR | 117; //复位USART2RCC-APB1RSTR ~(117); //停止复位 if(USART2_TX PA2){gpio_init(PA2,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZ }else if(USART2_TX PD5){RCC-APB2ENR | 10; //使能I/O复用时钟gpio_init(PD5,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZAFIO-MAPR | 13; //USART2重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}if(USART2_RX PA3){gpio_init(PA3, GPI_DOWN,HIGH); //输入下拉PAout(3)0; }else if(USART2_RX PD6){gpio_init(PD6,GPI_DOWN,HIGH); //输入下拉PDout(6)0; AFIO-MAPR | 13; //USART2重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}#if EN_USART2_RX USARTx[usartn]-CR1|18; //位8 PEIE 校验中断使能 校验错误硬件PE位置1USARTx[usartn]-CR1|15; //位5: RXNEIE 使能接收中断
#endif#if EN_USART2_TX USARTx[usartn]-CR1|16; //位6TCIE 发送完成中断使能位 当SR中的TC硬件置1将产生中断
#endif break; //*****************************************************USART3********************************************************case USART_3:RCC-APB1ENR | 118; //使能USART3时钟RCC-APB1RSTR | 118; //复位USART3RCC-APB1RSTR ~(118); //停止复位 if(USART3_TX PB10){gpio_init(PB10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZ }else if(USART3_TX PC10){RCC-APB2ENR | 10; //使能I/O复用时钟gpio_init(PC10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZAFIO-MAPR ~(0x034); //位5~4清零AFIO-MAPR | 0x014; //USART3部分重映射 }else if(USART3_TX PD8){RCC-APB2ENR | 10; //使能I/O复用时钟gpio_init(PD8,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZAFIO-MAPR ~(0x034); //位5~4清零AFIO-MAPR | 0x034; //USART3完全重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}if(USART3_RX PB11){gpio_init(PB11, GPI_DOWN,HIGH); //输入下拉PBout(11)0; }else if(USART3_RX PC11){gpio_init(PC11,GPI_DOWN,HIGH); //输入下拉PCout(11)0; AFIO-MAPR ~(0x034); //位5~4清零AFIO-MAPR | 0x014; //USART3部分重映射 }else if(USART3_RX PD9){gpio_init(PD9,GPI_DOWN,HIGH); //输入下拉PDout(9)0; AFIO-MAPR ~(0x034); //位5~4清零AFIO-MAPR | 0x034; //USART3完全重映射 }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}#if EN_USART3_RX USARTx[usartn]-CR1|18; //位8 PEIE 校验中断使能 校验错误硬件PE位置1USARTx[usartn]-CR1|15; //位5: RXNEIE 使能接收中断
#endif#if EN_USART3_TX USARTx[usartn]-CR1|16; //位6TCIE 发送完成中断使能位 当SR中的TC硬件置1将产生中断
#endif break;
//***********************************************************UART4***********************************************case UART_4:RCC-APB1ENR | 119; //使能UART4时钟RCC-APB1RSTR | 119; //复位UART4RCC-APB1RSTR ~(119); //停止复位 if(UART4_TX PC10){gpio_init(PC10,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZ }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}if(UART4_RX PC11){gpio_init(PC11, GPI_DOWN,HIGH); //输入下拉PCout(11)0; }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}#if EN_UART4_RX USARTx[usartn]-CR1|18; //位8 PEIE 校验中断使能 校验错误硬件PE位置1USARTx[usartn]-CR1|15; //位5: RXNEIE 使能接收中断
#endif#if EN_UART4_TX USARTx[usartn]-CR1|16; //位6TCIE 发送完成中断使能位 当SR中的TC硬件置1将产生中断
#endif break;
//***********************************************************USART1***********************************************case UART_5: RCC-APB1ENR | 120; //使能UART5时钟RCC-APB1RSTR | 120; //复位UART5RCC-APB1RSTR ~(120); //停止复位 if(UART5_TX PC12){gpio_init(PC12,GPO_MULPUSH_PULL_50,HIGH); //复用推挽输出最大速度50MHZ }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}if(UART5_RX PD2){gpio_init(PD2, GPI_DOWN,HIGH); //输入下拉PDout(2)0; }else{ASSERT(0); //上诉条件都不满足直接断言失败了设置管脚有误}#if EN_UART5_RX USARTx[usartn]-CR1|18; //位8 PEIE 校验中断使能 校验错误硬件PE位置1USARTx[usartn]-CR1|15; //位5: RXNEIE 使能接收中断
#endif#if EN_UART5_TX USARTx[usartn]-CR1|16; //位6TCIE 发送完成中断使能位 当SR中的TC硬件置1将产生中断
#endif break; }//***************************************************************************************************************USARTx[usartn]-BRRmantissa; //波特率设置USARTx[usartn]-CR1|12; //位2RE 接收使能USARTx[usartn]-CR1|13; //位3TE 发送使能 USARTx[usartn]-CR1~(112); //位12M 定义字长08位 1:9位//USARTx[usartn]-CR3|17; //位7: DMAT使能发送 //USARTx[usartn]-CR3|06; //位6 DMAR禁止接收USARTx[usartn]-CR1|113; //位13UE 串口使能
} 2.4.2 串口接收中断 当开启串口接收中断时如果接收到数据串口状态寄存器RXNE位被设置为1接着就可以从数据寄存器读取到接收到的数据 #define USART_RX_LEN 1024 //定义接收到的字符串最大长度 最大不超过2^14-116383u16 USART1_RX_STA 0 ; //自定义接收状态寄存器
u16 USART2_RX_STA 0 ; //自定义接收状态寄存器
u16 USART3_RX_STA 0 ; //自定义接收状态寄存器
u16 UART4_RX_STA 0 ; //自定义接收状态寄存器
u16 UART5_RX_STA 0 ; //自定义接收状态寄存器 volatile USART_TypeDef *USARTx[5] {USART1,USART2,USART3,UART4,UART5}; //定义五个指针数组保存 USARTx 的地址/************************************************************************************************************************* Description: USART1中断函数* USARTx-SR(32) : 接收状态寄存器 7:TXE 6:TC 5RXNE发送缓冲区为空 发送完成标志位 接收到数据标志位(读取DR则自动清0)USARTx-DR(32) : 接收/发送数据缓冲区 只使用低8位当接收到回车(回车由两个字节组成回车符0x0D和换行符0x0A)表示一次接收数据完成***********************************************************************************************************************/
void USART1_IRQHandler(void)
{u8 temp;if(USART1-SR(0x15)) //接收到数据{temp USART1-DR;if((USART1_RX_STA0x8000)0) //接收数据未完成{ if(USART1_RX_STA0x4000) //已经接收到了0xOD{if(temp!0x0A) //接收到的数据有误 USART1_RX_STA0; //状态寄存器清零elseUSART1_RX_STA |0x8000; //接收完成了 } else //没有接收到0xOD{if(temp 0x0D) //接收到回车符第一个字符 0xOD{USART1_RX_STA|0x4000;}else //没有接收到0xOD {USART1_RX_BUF[USART1_RX_STA0x3FFF]temp; //保存数据USART1_RX_STA;if(USART1_RX_STA(USART_RX_LEN-1))USART1_RX_STA0; //接收数据超过USART_RX_LEN,则丢失数据重新接收 }} }}/*******************************************************************************************///可在这里自定义其他任务 /********************************************************************************************/
} 在上面代码中 当接收到回车(回车由两个字节组成回车符0x0D和换行符0x0A)表示一次接收数据完成接收到的数据存放在字符数组USART1_RX_BUF。 /***************************************************************************************************************** USARTx_RX_BUF[USART_RX_LEN]:保存接收的数据* USARTx_RX_STA :自定义16位接收状态寄存器 bit15 bit14 bit13~0接收完成标志 接收到0xOD标志 接收到有效数据的个数* 当接收到回车(回车由两个字节组成回车符0x0D和换行符0x0A)表示一次接收数据完成***************************************************************************************************************/
u8 USART1_RX_BUF[USART_RX_LEN]; //接收缓冲区
u8 USART2_RX_BUF[USART_RX_LEN]; //接收缓冲区
u8 USART3_RX_BUF[USART_RX_LEN]; //接收缓冲区
u8 UART4_RX_BUF[USART_RX_LEN]; //接收缓冲区
u8 UART5_RX_BUF[USART_RX_LEN]; //接收缓冲区 2.4.3 串口发送 /***************************************************************************************************************** 函数名称uart_putchar* 功能说明串口发送一个字节* 参数说明USARTn 模块号(USART_1~USART_5)* 函数返回无*
***************************************************************************************************************/
void usart_putchar (USARTn usartn, char ch)
{//等待发送缓冲区空while(!(USARTx[usartn]-SR(17))); //等待TXE1 发送缓冲区为空 数据已转到移位寄存器//发送数据USARTx[usartn]-DRch;
// while(!(USARTx[usartn]-SR(15))); //等待TC1发送完成
}/****************************************************************************************************************
*
* 函数名称uart_sendStr
* 功能说明串行发送字符串
* 参数说明USARTn 模块号(USART_1~USART_5)
* str 字符串
* 函数返回无
*
*************************************************************************/
void usart_sendStr (USARTn usartn, const u8 *str)
{while(*str){usart_putchar(usartn, *str);}
}/****************************************************************************************************************
*
* 函数名称uart_sendNum
* 功能说明串行发送字符串
* 参数说明USARTn 模块号(USART_1~USART_5)
* num 无符号整数
* 函数返回无
*
*************************************************************************/
void usart_sendNum(USARTn usartn , u32 num)
{u8 ch[32];u8 i 0;IntToStr(num, ch);while(ch[i]){usart_putchar(usartn, ch[i]);}
} 2.4.4 重定向printf函数 重定向这个C库的printf函数修改printf函数底层调用fputc //加入以下代码,支持printf函数,而不需要选择use MicroLIB
/***************************START********************************************************************/
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle; /* Whatever you require here. If the only file you are using is */ /* standard output using printf() for debugging, no file handling */ /* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{ x x;
}
//重定义fputc函数 默认使用的是USART1口
int fputc(int ch, FILE *f)
{ USART1-DR (u8) ch; while((USART1-SR0x40)0); //循环发送,直到发送完毕 TC1return ch;
}
#endif
/***************************END*******************************************************************************/ 2.4.5 输出接收数据 如需使能串口1接收数据我们只需要在main函数添加如下代码 STM32_NVIC_Init(2,USART1_IRQn,0,1); //必须配置其中包括中断使能
usart_init(USART_1,115200); //串口1初始化波特率9600 映射到PA9 PA10while(1)
{ // 数据接收完成while(USART1_RX_STA0x8000){// 遍历数据for(i0;i(USART1_RX_STA0x3FFF);i){ usart_putchar(USART_1,USART1_RX_BUF[i]);}usart_putchar(USART_1,\n);// 清空接收标志位 USART1_RX_STA0;}delay_ms(1500);
} 2.5.6 烧录测试 编译完成后通过ISP烧录到开发版DTR的低电平复位RTS高电平进BootLoader。 由于ISP烧录使用的也是串口1因此可以使用usb线连接到开发板ISP接口然后打开串口助手。即可通过ISP接口与串口1进行数据交互 这里有几点需要注意 如果我们发送的是HEX一行数据的结尾我们需要追加0x0D(\r)0x0A(\n)中断程序不能执行耗时长的程序否则可能导致后面的数据到达的时候无法接受会造成只接收到一个/两个字节的现象因此需要避免在USART1_IRQHandler中使用printf函数。 三、源码下载 源码下载路径stm32f103。 参考文章 [1] STM32F103时钟系统讲解