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

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

前言

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

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

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、按键驱动移植

因为自己以前用到了一个非常好用的按钮设计,所以一直保留至今,直接上.c 和 .h文件

/*
2019/5/21 按键程序移植成功,以后可以使用此按键,需要研究一下
和以前单片机项目按钮方式类似  
by  qzh
2019/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接口测试。

### 回答1: STM32L476是一款微控制器,拥有64KB随机存储器和1MB闪存。其原理图是描述其电路连接和工作方式的图表,故在进行STM32L476开发和电路设计时对原理图的了解是非常重要的。 STM32L476原理图包含了主控芯片的引脚连接以及外围器件的接法,如晶振、电源、信号放大器、I/O接口等。其结构清晰,便于用户理解其完整的系统设计,并通过电路接线指导用户进行硬件搭建和调试。 在STM32L476原理图中,通过图中各个元器件的连接方式,可以清晰地了解各模块之间的工作原理。例如,运放模块中的电容、电阻、极管等元件,就是用户用来调整放大倍数、滤波等电路参数的基本元器件。此外,将原理图与应用说明手册配合使用,用户能够更深入地了解如何对芯片进行程序编写,从而实现各种功能。 总之,STM32L476原理图是STM32L476微控制器设计中非常重要的组成部分。其能够使开发者和用户更好地了解整个系统结构和功能部件的接线方式,进而更好地进行开发设计、硬件搭建和调试。 ### 回答2: STM32L476是一款低功耗高性能的微控制器芯片,在嵌入式系统中有着广泛的应用。STM32L476的原理图主要由微控制器芯片、外设电路、调试器电路和时钟电路等组成。 微控制器芯片是STM32L476原理图的核心部分,它包含了运行内核、存储程序和数据的核心处理器、存储器和控制外设的接口电路等。外设电路包括串口、SPI、I2C等外部设备接口电路。这些电路通过GPIO与微控制器芯片相连。调试器电路包括JTAG电路和SWD调试电路,可以通过这些接口进行调试和下载程序。时钟电路主要用于提供微控制器芯片运行所需的时钟信号,其中包括晶振和时钟电路。 整个STM32L476原理图设计合理,芯片与外设接口清晰,如UART、I2C、SPI等,易于外部组件的添加和硬件调试。同时,针对低功耗特点,原理图中还加入了RTC电路及低功耗模式的控制电路,可以配合STM32L476的特性实现深度睡眠等低功耗模式。此外,还加入了四个LED指示灯,便于硬件调试和代码测试。 综上所述,STM32L476原理图是一个高性能、低功耗的嵌入式系统设计方案,可以应用于各种嵌入式系统设计中。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

矜辰所致

您们的鼓励是我奋斗的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值