本帖最后由 wolfgang2015 于 2017-6-17 10:51 编辑
一、实验开始之前二、实验过程0、上次实验回顾 上次实验中,我们通过STM32CubeMX创建了通过按键捕获中断,实现LED变频闪亮的工程,通过不断按下用户键实现LED,让LED从延时512开始,逐次减半延时至0的规律循环。 回顾实验,有几个核心函数出现在试验中: 1、按键功能并没有接其它元器件,只使用了MCU上GPIO的中断捕获功能,用到的中断回调函数 void HAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin),在中断使能情况下,当捕获到了对应中断,进入中断回调函数中,执行中断代码逻辑。在HAL库中。
2、调用这个回调函数的函数为: void HAL_GPIO_EXTI_IRQHandler(uint16_tGPIO_Pin) 而, 这个函数是由void EXTI4_15_IRQHandler(void)中调用,EXTI4_15_IRQHandler是在启动汇编文件中的中断向量表中供ARM系统调用。当ARM系统产生中断而没有被屏蔽,立即会触发中断函数中的内容。 3、GPIO口的相关定义,B1_Pin、B1_GPIO_Port GPIO_InitStruct.Pin = B1_Pin;
|
这几个引脚的定义在{项目名称}\Inc\ mxconstants.h 文件中,定义内容如下:
#define B1_Pin GPIO_PIN_13 #define B1_GPIO_Port GPIOC
|
GPIO_PIN_13的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中
....... #define GPIO_PIN_13 ((uint16_t)0x2000U) /* Pin 13 selected */ ......
|
GPIOC的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h 文件中
......... #define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address */ ......... #define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000) ........ #define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000) ....... #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) .......
|
这里我们可以发现GPIOA是一个32位的寄存器,每个GPIO引脚都由这个积存器的bit来控制,而且这个寄存器的基地址由GPIOA_BASE 得来,GPIOA_BASE这个基地址是由AHB2PERIPH_BASE + 0x0000得来,AHB2PERIPH_BASE这个基地址是由 PERIPH_BASE +0x08000000得来;PERIPH_BASE这个基地址是由(uint32_t)0x40000000。 这种用基址+偏移的方法来定义常量很不错,寄存器往往有一个固定的地址,如果编译、移植,如果偏移不变,只需要更换基址即可。
GPIO_TypeDef结构体是在{项目名称}\Drivers\CMSIS\Device\ST\STM32L4xx\Include\stm32l476xx.h
typedef struct { __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ __IO uint32_t BRR; /*!< GPIO Bit Reset register, Address offset: 0x28 */ __IO uint32_t ASCR; /*!< GPIO analog switch control register, Address offset: 0x2C */ } GPIO_TypeDef;
|
GPIO_PIN_SET、GPIO_PIN_RESET的定义在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中
4、GPIO口的模式: [url=]上升沿触发[/url]
...... GPIO_InitStruct.Mode=GPIO_MODE_IT_RISING; ......
|
GPIO_MODE_IT_RISING的定义在 {项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h 文件中
.... #define GPIO_MODE_IT_RISING (0x10110000U) /*!< External Interrupt Mode with Rising edge trigger detection */ ......
|
5、外部中断初始化并中断使能。
...... HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI4_15_IRQn); ......
|
中断的相关初始化和使能,在{项目名称}\Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_cortex.h 文件中。
1、新实验设想 GPIO的输出、中断捕获功能原理熟悉之后,可以进一步设想,能否对实验进行改进,利用CPU的定时器作为参照灯或心跳灯,检测程序是否运行;并利用串口将按键后改变的延时值输出到上位机进行展示。 A)定时器设置: 使用CubeMX打开芯片引脚设置,将定时器1的时钟源设置为内部时钟。
系统时钟频率设置为80MHz
在定时器功能中设置如下: 设置预分频40000-1(0~39999),计数时间1000-1(0~1999),确定每次中断计数就是一个2000次计数,每次计数是1ms,。
定时器中断使能。
B)串口设置: 选择功能树上Usart2,为异步模式,VCP仅使用了Rx和Tx 并无流控,因此硬件流控为禁用: 设定PIN脚 PA2为Usart_TX,PA3为Usart_RX。
在串口功能中设置如下:
波特率:115200,数据长度,8bits,起停位1bit,其他的保持默认
USART2 中断使能
GPIO口及标签设置如上图 设置完以上内容后,通过CubeMX创建试验代码。
CubeMX创建的实验代码有专门的章节介绍,这里介绍分析省略。
2、实验步骤 A)定时器设置: 定时器设置虚要外界一个LED或者蜂鸣器,通过GPIO F4口来进行控制 先设置好定时器的回调函数,通过回调函数控制LED的闪亮和熄灭: 在mxconstants.h 文件定义定时器的GPIO引脚,GPIO的相关设置参考前面的设置方法:
...... #define LDTimer_Pin GPIO_PIN_4 #define LDTimer_GPIO_Port GPIOF .......
|
在main.h设置定时器控制的LED相关控制函数
...... #define TIMERLED(STA) (STA)?HAL_GPIO_WritePin(LDTimer_GPIO_Port,LDTimer_Pin,GPIO_PIN_SET):HAL_GPIO_WritePin(LDTimer_GPIO_Port,LDTimer_Pin,GPIO_PIN_RESET) ......
|
定义定时器回调函数中的内容:
....... static uint8_t staTimer = ON; ...... void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { TIMERLED(staTimer); staTimer = !staTimer; } ......
|
在main()函数中主循环里设置启动定时器: ....... //TIME启动; HAL_TIM_Base_Start_IT(&htim1); ......
|
B)串口设置: 先设置串口的发送数据缓冲区和接收缓冲区 在main.h 文件定义发送和接收缓冲区的大小:
...... #define RxBufferSize 64 #define TxBufferSize 64 .......
|
在main.c文件中定义发送和接收缓冲区的数组: ...... static uint8_t Rx_Buffer[RxBufferSize] = { 0 }; static uint8_t Tx_Buffer[TxBufferSize] = { 0 }; .......
|
在非DMA方式下实现串口通信时需要设置以下内容: 在Usart2的初始化函数(MX_USART2_UART_Init)中,设置中断和数据接收缓冲区 ...... HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize); .......
|
在Main.c文件中,设置好串口中断回调函数: ........ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ if (huart==&huart2){ //printf("Rx: %s \n",Rx_Buffer); //设置好Printf方式输出后可去掉注释符号 .........//捕获中断后,将接收到的缓冲数据,进行数据处理的函数(这里略) HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize); } } .......
|
在Main.c文件中,设置好发送数据函数 ........ static uint8_t *TxBuffer; static uint32_t StrN; //设置动态字符串长度值 ........ void SendWithOutDMA_Len(void) { TxBuffer = (uint8_t *) malloc(StrN * sizeof(uint8_t)); .......//组织TxBuffer 将发送的字符串拼接起来; HAL_UART_Transmit(&huart2, TxBuffer, StrN, 0xff); free(TxBuffer); } .......
|
将 SendWithOutDMA_Len();函数放到需要发送数据的功能下便可实现串口发送数据; 这里将函数放在按键响应函数中: ........ void KeyPass(void) { ........ //按键发送数据 SendWithOutDMA_Len(); ...... } .......
|
通过Printf函数进行串口输出设置: ........ /* USER CODE BEGIN Includes */ ....... #ifdef __GNUC__ /* With GCC, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */ ...... /* USER CODE END Includes */ .......
/* USER CODE BEGIN 4 */ ........ /* retarget the C library printf function to the USART */ PUTCHAR_PROTOTYPE { /* Place your implementation of fputc here */ /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */ HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF); return ch; } ....... /* USER CODE END 4 */
|
经过以上设置之后,即可用printf函数向串口输出字符串了 printf("HelloSTM32L476RC! \n"); 3、实验的观察结果 A)定时器: 外接的LED(注意串接一个分压电阻)每一秒钟闪亮一次; B)串口: 可通过SendWithOutDMA_Len()函数向串口输出信息; 也可通过Printf("xxxxxxx")函数向串口输出信息; 三、实验后的心得
这个实验,可以了解到定时器回调函数、串口回调函数的使用方式,在串口发送数据的实验中,还体验到串口不同的数据发送方式(使用中断方式方式发送和接收数据,使用Printf函数发送数据)。
此前以为发了次文章,后来检查文章时才发现忘发此帖了(补一个) 界此,基于Nucleo系列板卡的试验基础环境搭建好了,之后回基于这些基本LED、按键、中断、串口、定时器的基本功能做一系列的试验,下一个系列是低功耗系列
后文连接:
1、ST线下培训(05-23成都站)STM32L476低功耗设计(一)
|