写点什么

STM32L051 测试 (二、开始添加需要的代码)

作者:矜辰所致
  • 2022-10-11
    江苏
  • 本文字数:4669 字

    阅读完需:约 1 分钟

STM32L051测试 (二、开始添加需要的代码)
STM32L051 测试系列文章,第二课,基于CubeMX生成的工程,我们开始添加代码进行基本的测试。
复制代码


前言

在第一课我们完成了使用 CubeMX 生成工程,那么在生成的工程上我们需要做一些简单的基本测试。


本测试系列博文:

STM32L051 测试 (一、使用 CubeMX 生成工程文件 — ST 系列芯片通用)

https://xie.infoq.cn/article/fc5e31ea41e0b33f64b9ee537

1、LED 灯的闪烁

在主函数 while 循环中直接使用延时控制:


  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);  HAL_Delay(1000);
复制代码


测试正常,这个延时 1S 钟靠肉眼识别,如果有误差其实也看不出来,影响 delay 应该是和系统时钟有关,后续再确定是否正常;

2、定时器控制 LED 闪烁

在 tim.c 文件中对应处添加 HAL_TIM_PeriodElapsedCallback 函数,这是定时器中断的响应函数,当然,参数的定义(这里使用的参数只不过是以前使用 F103 保留下来直接复制过来的,我这里就没有进行对应的处理),相关.h 文件的包含不要忘记添加。


还有一个需要注意的,定时器初始化了,需要在主函数中开启定时器。


所以进行以下操作,LED 会每隔 3S 切换一次


/* USER CODE BEGIN 2 */  HAL_TIM_Base_Start_IT(&htim2);  /* USER CODE END 2 */
复制代码


/* USER CODE BEGIN 1 */void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){  if(htim->Instance==TIM2){    Timer3_count++;    if(Timer3_count >= 3){      Timer3_count = 0;      HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);        }         }  else if(htim->Instance==TIM21){    ++Timer4_count;      if(Timer4_count>0X1FFFFFF)Timer4_count=0;      }}/* USER CODE END 1 */
复制代码

3、串口相关

3.1 printf 函数的实现

/* USER CODE BEGIN 0 */#if 1#include <stdio.h>
/* 告知连接器不从C库链接使用半主机的函数 */#pragma import(__use_no_semihosting)
/* 定义 _sys_exit() 以避免使用半主机模式 */void _sys_exit(int x){ x = x;}
/* 标准库需要的支持类型 */struct __FILE{ int handle;};
FILE __stdout;
int fputc(int ch, FILE *stream){ /* 堵塞判断串口是否发送完成 */ while((USART1->ISR & 0X40) == 0);
/* 串口发送完成,将该字符发送 */ USART1->TDR = (uint8_t) ch;
return ch;}#endif/* USER CODE END 0 */
复制代码


其中需要说明的是,如果是 F103 ,不是 ISR 和 TDR 寄存器,而是 SR DR 寄存器。

3.2 串口接收不定长度的数据

在 usart.c 文件中添加 HAL_UART_RxCpltCallback 函数,对应的缓存数组不要忘记定义,我们这里使用的是 LPUART1 和 我的无线通讯模块通讯(以前 F103 对应的引脚是串口 3)。


在中断响应函数中把串口接收到的数据存到 USART_Enocean_BUF 中,然后在主函数中实现打印接收到的一串数据。


/* USER CODE BEGIN 1 */void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){  if(huart->Instance == LPUART1){    Enocean_Data++;    HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1);  }  else if(huart->Instance==USART1)  {      // HAL_UART_Transmit_IT(&huart1,(uint8_t *)USART1_BUF, 10);       // HAL_UART_Receive_IT(&huart1, (uint8_t *)USART1_BUF, 10);    }}/* USER CODE END 1 */
复制代码


  /* Infinite loop */  /* USER CODE BEGIN WHILE */  while (1)  {    if(test_data != Enocean_Data){        HAL_Delay(7);        HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF);     //将串口3接收到的数据通过串口1传出           memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF));   //清空缓存区         Enocean_Data=0;        (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//这一句很重要,没有这一句,后面接收会出错    }    /* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
复制代码


和定时器一样要注意,串口开启中断接收,需要在初始化后执行一次中断接收的函数 HAL_UART_Receive_IT ,类似于开启中断接收:


  /* USER CODE BEGIN 2 */  HAL_TIM_Base_Start_IT(&htim2);  //使能串口中断接收  HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);  /* USER CODE END 2 */
复制代码


完成了以上操作,就能够实现将 LPUART1 收到的数据,通过串口 1 打印出来


今天还发现一个细节,HAL_UART_RxCpltCallback 函数不需要再次在.h 文件中申明,因为 HAL 库中虽然是 _weak 声明的,但是在底层 stm32L0xx_hal_uart.h 中已经申明了,所以在应用程序中申不申明都可以 。

4、独立看门狗

看门狗还是比较简单的,直接在循环中加一个喂狗函数就可以:


  HAL_IWDG_Refresh(&hiwdg);  }  /* USER CODE END 3 */
复制代码


这里我也测试了下上一篇文章我设置的看门狗时间,当时计算出来看门狗时间为 6.4S,是准确的。

5、按键驱动移植

因为自己以前用到了一个非常好用的按钮设计,所以一直保留至今,可参考文章:


开源一夏 | 单片机系统中实用的按键驱动(STM32、51 都适用)

https://xie.infoq.cn/article/e6bc8e57f11b929430823c5c3


直接上.c 和 .h 文件


/*2019/5/21 按键程序移植成功,以后可以使用此按键,需要研究一下和以前单片机项目按钮方式类似  by  qzh2019/8/30  确定了第三行,第一个必须是7,才能按下到时间自动触发by  qzh*/#include "mod_button.h"

//GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)void io_getDigital(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin,uint8 *pu8Value){ *pu8Value = HAL_GPIO_ReadPin(GPIOx,GPIO_Pin);}
void time_setTimerCount(TIMER_TYPE *pu8timer,uint32 u32timeToCount) { // __HAL_TIM_SET_COUNTER HAL_TIM_Base_Start_IT(&htim21); // HAL_TIM_Base_Stop_IT // TIM_Cmd(TIM4, ENABLE); if(pu8timer->on == 0) pu8timer->on = 1; if(pu8timer->on == 1) //IntNum = 0; pu8timer->timeInit = Timer4_count; //pu8timer->timeInit = IntNum; pu8timer->timeOut = 0; pu8timer->timeToCount = u32timeToCount;}
RETURN_TYPE time_getTimeOut(TIMER_TYPE *pu8timer){ uint32 Temp_Val; if(Timer4_count > pu8timer->timeInit) Temp_Val = Timer4_count - pu8timer->timeInit; else Temp_Val = (0xFFFFFFFF-pu8timer->timeInit)+Timer4_count; if(Temp_Val >= pu8timer->timeToCount) { pu8timer->timeOut = 1; pu8timer->on = 0; pu8timer->timeToCount = 0; pu8timer->timeInit = 0; } else pu8timer->timeOut = 0; return (pu8timer->timeOut == 1)?TIME_OUT:OK;}
BTN_STATE btn_getState(BTN_STRUCT *pBtn){const uint8 transition_table[8][4]={ 0, 1, 0, 1, 5, 2, 5, 1, 7, 2, 5, 3, 5, 4, 5, 4, 5, 4, 5, 4, 6, 1, 0, 1, 6, 1, 7, 1, 0, 1, 0, 1 }; //register uint8 u8Input; uint8 u8Input; // Get button state io_getDigital(pBtn->u8Pin,pBtn->GPIO_Pin ,&u8Input); u8Input = (u8Input == pBtn->u8ActiveState)?1:0; // Get timeout state u8Input |= ((time_getTimeOut(&(pBtn->tTimer))==TIME_OUT)?2:0);
// Get new state pBtn->u8State = transition_table[pBtn->u8State][u8Input]; // we want only the state, not action
// Perform action switch (pBtn->u8State) { case 1: time_setTimerCount(&(pBtn->tTimer), pBtn->u16TimeOutON); break; case 5: time_setTimerCount(&(pBtn->tTimer), pBtn->u16TimeOutOFF); break; } // return pBtn->u8State; //待测试 return (BTN_STATE)pBtn->u8State; }

void Button_Action(){ /* 按键动作,模式选择 */}
复制代码


#ifndef _MOD_BUTTON_H_INCLUDED#define _MOD_BUTTON_H_INCLUDED
#include "main.h"#include "Datadef.h"#include "tim.h"/* Timeout ON _______|_____ P | | Timeout OFF R ___________| |________|____ ^ ^ ^ ^ ^ ^ ^ ^ S 0 1 2 3 4 5 6 7
P - pressed, R - released, S - BTN_STATE*/
/*°´Å¥Ïà¹Ø KEY1 learn PB5 KEY2 CLEAR PB6*/#define BTN_ACTIVE 0 //when pressed, switch to GND

typedef struct{ // Public //uint8 u8Pin; // e.g. ADIO0 //uint16 u8Pin; // e.g. ADIO0 GPIO_TypeDef * u8Pin; uint16_t GPIO_Pin; uint8 u8ActiveState; // button is pressed if (io_getDigital(u8Button)==bActiveState) uint16 u16TimeOutON; // time the button has to be pressed to be recognized as pressed uint16 u16TimeOutOFF; // time the button has to be pressed to be recognized as released // Private TIMER_TYPE tTimer; uint8 u8State;
} BTN_STRUCT;
typedef enum { BTN_IDLE = 0, BTN_EDGE1, BTN_TRIGGERED, BTN_PRESSED, //< most important BTN_PRESS_HOLD, BTN_EDGE2, BTN_RELEASE_HOLD, BTN_RELEASED } BTN_STATE;
extern u16 Timer4_count;
BTN_STATE btn_getState(BTN_STRUCT *pBtn);void time_setTimerCount(TIMER_TYPE *pu8timer,uint32 u32timeToCount); void io_getDigital(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,uint8 *pu8Value);RETURN_TYPE time_getTimeOut(TIMER_TYPE *pu8timer);

#endif //_MOD_BUTTON_H_INCLUDED
复制代码


在主函数中添加需要用到的按钮操作:


    /* USER CODE BEGIN 3 */    if(btn_getState(&K1_BUTTON_150mS) == BTN_EDGE2){        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);    }
if((btn_getState(&K1_BUTTON_2S)==BTN_PRESSED)){ while(btn_getState(&K1_BUTTON_150mS)); }
if(btn_getState(&K2_BUTTON_150mS) == BTN_EDGE2){ HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); HAL_Delay(150); HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } HAL_IWDG_Refresh(&hiwdg); } /* USER CODE END 3 */
复制代码


测试结果 OK。

6、软件复位

/* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void SoftReset(void){    __set_FAULTMASK(1); // 关闭所有中断    NVIC_SystemReset(); // 复位}/* USER CODE END 0 */
复制代码


其实上面这个是有问题的,在 HAL 库中没有__set_FAULTMASK(1)这个,直接如下:


/* USER CODE BEGIN 0 */void SoftReset(void){    HAL_NVIC_SystemReset(); // 复位}/* USER CODE END 0 */
复制代码


或者直接用 HAL_NVIC_SystemReset();这个函数在程序中就可以;


结语


本文把基本上项目上使用到的串口,LED,按钮等功能都测试过了,然后我还得把通讯模块的底层一些驱动移植过来,其实也就是根据通讯模块的串口协议进行的一系列操作。


这里就不说明,等今天完成这部分,下一篇文章会来写一下通过 IO 口,软件模拟的 I2C 接口测试。

发布于: 2022-10-11阅读数: 24
用户头像

矜辰所致

关注

CSDN、知乎、微信公众号: 矜辰所致 2022-08-02 加入

不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开! 为了活下去的嵌入式工程师,画画板子,敲敲代码,玩玩RTOS,搞搞Linux ...

评论

发布
暂无评论
STM32L051测试 (二、开始添加需要的代码)_stm32_矜辰所致_InfoQ写作社区