【MPS探索营+创意实现】+智能单车灯05焊接调试

上个帖子介绍了PCB设计过程,这个帖子介绍PCB焊接和程序调试过程。

PCB焊接前先进行检查,确保所使用的裸板是合格的,不能有断线缺焊盘等问题,否则等焊完再发现PCB问题,先前的工作前功尽弃了。裸板如下图。

图1、裸板

第一步先啃最难的骨头,就是MP2731。我在做封装的时候为了方便手工焊接,有意把引脚焊盘做的稍微长一点,这样对于QFN焊接更容易上锡。经过一番努力,终于把芯片焊上去了,用万用表测试没有短路。如下图。

图2、焊接MP2731

然后焊接其他部分芯片。其余的部分封装比较大,基本上没什么问题,相对容易焊好了,如下图。

图3、焊接完成

焊完的板子用万用表测试后没有短路现象。然后就是上电测试。接上电池,用万用表测试电压正常,如下图。

图4、电压正常

观测电源输出,在5.5V时,充电电路工作电流大约在468mA,基本和默认设置一致。如下图。

图5、电流正常

电压电流都正常,基本上说明板子焊接没有太大问题。然后接上单片机的调试工具ST-link调试程序,如下图。

图6、调试程序

第一个要操作的肯定是MP2731。我先是用ST官方自带的I2C驱动连接MP2731,但是不管我怎么更改I2C配置,反复尝试就是不能连接上。开始怀疑MP2731没焊好,各种补焊,从引脚上锡程度应该是没问题的。各种尝试不成功后,我想起开发板配的调试工具也可以操作MP2731,然后我赶紧把I2C的两个引脚和GND引出来连接到开发板调试器上,如下图。

图7、连接调试器

在PC端配置完成后一把通讯成功,如下图。这说明我焊接是没有问题的,应该是程序的问题,这样我就放心硬件了,开始仔细研究怎么搞定程序设置。

图8、MP2731通讯成功

网上查到ST官配的I2C代码适用性比较窄,大部分工程师建议自己写代码。因此我决定自己写MP2731的I2C驱动代码,不再考虑库程序了。经过几个晚上的奋战和反复测试,终于搞出完全好用的MP2731驱动代码,整个C文件如下。

代码1、MP2731驱动代码

//i2c.c

#include "i2c/bsp_i2c.h"
 
static void i2c_Delay(void)
{
	uint8_t i;
	for (i = 0; i < 4; i++);
}
//开始信号
void i2c_Start(void)
{
	// 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 
	I2C_SDA_1();
	I2C_SCL_1();
	i2c_Delay();
	I2C_SDA_0();
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();
}
 
//停止信号
void i2c_Stop(void)
{
	// 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 
	I2C_SDA_0();
	I2C_SCL_1();
	i2c_Delay();
	I2C_SDA_1();
	i2c_Delay();
}
//发送一个字节
void i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;
	
	// 先发送字节的高位bit7 
	for (i = 0; i < 8; i++)
	{
		if (_ucByte & 0x80)
		{
			I2C_SDA_1();
		}
		else
		{
			I2C_SDA_0();
		}
		i2c_Delay();
		I2C_SCL_1();
		i2c_Delay();
		I2C_SCL_0();
		if (i == 7)
		{
			 I2C_SDA_1(); 	// 释放总线
		}
		_ucByte <<= 1;		// 左移一个bit 
		i2c_Delay();
	}
}
//读取一个字节
uint8_t i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value;
	
	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		I2C_SCL_1();
		i2c_Delay();
		if (I2C_SDA_READ())
		{
			value++;
		}
		I2C_SCL_0();
		i2c_Delay();
	}
	return value;
}
 
//等待应答信号
uint8_t i2c_WaitAck(void)
{
	uint8_t re;
 
	I2C_SDA_1();	/* CPU释放SDA总线 */
//	i2c_Delay();
	I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	i2c_Delay();
 
	if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
 
	I2C_SCL_0();
	i2c_Delay();
	return re;
}
//应答信号
void i2c_Ack(void)
{
	I2C_SDA_0();	/* CPU驱动SDA = 0 */
	i2c_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();
	I2C_SDA_1();	/* CPU释放SDA总线 */
}
void i2c_Nack(void)
{
	I2C_SDA_1();	/* CPU驱动SDA = 0 */
	i2c_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	I2C_SCL_0();
	i2c_Delay();
	I2C_SDA_1();	/* CPU释放SDA总线 */
}
 
uint8_t i2c_CheckDevice(uint8_t _Address)
{
	uint8_t ucAck;
 
	if (I2C_SDA_READ() && I2C_SCL_READ())
	{
		i2c_Start();		/* 发送启动信号 */
 
		/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
		i2c_SendByte(_Address | 0);
		ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */
 
		i2c_Stop();			/* 发送停止信号 */
 
		return ucAck;
	}
	return 1;	/* I2C总线异常 */
}

uint8_t Device_ReadData(uint8_t DeciveAddr,uint8_t RegAddr,uint8_t *Data,int size)
{
    uint8_t ucAck;
    
    int count=size;
		uint8_t *pData=Data;
		//起始信号
    i2c_Start();    
    //发送器件地址                         
    i2c_SendByte(DeciveAddr<<1);       
    //等待应答
    ucAck = i2c_WaitAck(); 
    if(ucAck)  return ucAck;
    i2c_SendByte(RegAddr);
    ucAck = i2c_WaitAck(); 
    if(ucAck)  return ucAck;
    i2c_Start(); 
    i2c_SendByte((DeciveAddr<<1)+1); 
    ucAck = i2c_WaitAck(); 
    if(ucAck)  return ucAck;
    while(count--)
		{
			//发送数据
			*pData++ = i2c_ReadByte();   
            if(count)
            {
               i2c_Ack(); 
            }
            else
            {
                i2c_Nack(); //end nack
            }
		}			 
                       
    //结束信号
    i2c_Stop(); 
    return ucAck; 
}

uint8_t Device_WriteData(uint8_t DeciveAddr,uint8_t RegAddr,uint8_t *Data,int size)
{
    uint8_t ucAck;
    
    int count=size;
		uint8_t *pData=Data;
		//起始信号
    i2c_Start();    
    //发送器件地址                         
    i2c_SendByte(DeciveAddr<<1);  //     
    //等待应答
    ucAck = i2c_WaitAck(); 
    if(ucAck)  return ucAck;
    i2c_SendByte(RegAddr);       //
    ucAck = i2c_WaitAck(); 
    if(ucAck)  return ucAck;

    while(count--)
		{
			//发送数据
			i2c_SendByte(*pData++);                     
			//等待应答
            ucAck = i2c_WaitAck(); 
            if(ucAck)  return ucAck;   
		}			 
                       
    //结束信号
    i2c_Stop(); 
    return ucAck; 
}

搞定了MP2731,马上把LED灯、蜂鸣器、电池、按键、震动传感器等外设焊接好,开始弄整体的程序。整体电路如下图。

图9、整体电路

这些外设以前都搞过,比较简单,就是有点繁琐,大概弄了几个晚上基本都搞定了。下面是截取的部分代码,仅供参考。

代码2、部分代码

void LED_PWM_SET(uint8_t level)
{
    if(0 == level)
    {
        /* Stop channel 1 */
        if (HAL_TIM_PWM_Stop(&htim14, TIM_CHANNEL_1) != HAL_OK)
        {
            /* PWM Generation Error */
            Error_Handler();
        } 
    }
    else
    {
        /* Stop channel 1 */
        if (HAL_TIM_PWM_Stop(&htim14, TIM_CHANNEL_1) != HAL_OK)
        {
            /* PWM Generation Error */
            Error_Handler();
        } 
        if(1 == level)
        {
            sConfigOC.Pulse = PULSE4_VALUE;
        }
        else if(2 == level)
        {
            sConfigOC.Pulse = PULSE3_VALUE;
        }
        else if(3 == level)
        {
            sConfigOC.Pulse = PULSE2_VALUE;
        }
        else
        {
            sConfigOC.Pulse = PULSE1_VALUE;
        }
        if (HAL_TIM_PWM_ConfigChannel(&htim14, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
        {
            Error_Handler();
        }
        /* Start channel 1 */
        if (HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1) != HAL_OK)
        {
            /* PWM Generation Error */
            Error_Handler();
        }
    }
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
  PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.Timing = I2C_TIMING;
  hi2c1.Init.OwnAddress1 = I2C_ADDRESS;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0xFF;
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief TIM3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC2 = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = uhPrescalerValue;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = PERIOD_VALUE;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC2.OCMode = TIM_OCMODE_PWM1;
  sConfigOC2.Pulse = PULSE1_VALUE;
  sConfigOC2.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC2.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC2, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC2, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);
  /* Start channel 1 */
//  if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2) != HAL_OK)
//  {
//    /* PWM Generation Error */
//    Error_Handler();
//  }
}

/**
  * @brief TIM14 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM14_Init(void)
{

  /* USER CODE BEGIN TIM14_Init 0 */

  /* USER CODE END TIM14_Init 0 */

//  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM14_Init 1 */

  /* USER CODE END TIM14_Init 1 */
  htim14.Instance = TIM14;
  htim14.Init.Prescaler = 0;
  htim14.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim14.Init.Period = PERIOD_VALUE;
  htim14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim14) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim14) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = PULSE4_VALUE;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim14, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM14_Init 2 */

  /* USER CODE END TIM14_Init 2 */
  HAL_TIM_MspPostInit(&htim14);
  
  /* Start channel 1 */
  if (HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1) != HAL_OK)
  {
    /* PWM Generation Error */
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);

  /*Configure GPIO pins : PA0 PA1 PA2 PA15 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PA3 PA4 PA5 PA6 */
  GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;//|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB1 PB3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : PA8 PA9 PA10 */
  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PA11 PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);

  HAL_NVIC_SetPriority(EXTI2_3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);

  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);

  
  
    /*Configure GPIO pins : PB6 PB7*/
  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
//  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
//  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7|GPIO_PIN_6, GPIO_PIN_SET);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == KEY_PWR_Pin)
    {
        LEDx_StateSet(LED1,LED_TOGGLE);
    }
    if(GPIO_Pin == KEY_BEEP_Pin)
    {
        LEDx_StateSet(LED2,LED_TOGGLE);
    }
    if(GPIO_Pin == SHOCK_INT_Pin)
    {
        LEDx_StateSet(LED3,LED_TOGGLE);
    }
    if(GPIO_Pin == MP_INT_Pin)
    {
        LEDx_StateSet(LED4,LED_TOGGLE);
    }
    
}

经过断断续续调试,基本上把预期的功能都调了一下,这个作品基本完成。

通过本次作品制作,熟悉了MP2731这个芯片的强大功能,对于大多数基于锂电池的应用,基本上一颗MP2731加一颗最简单的MCU就能满足需求。MP2731自带的各种电压、电流、温度等监测功能,既能节省MCU资源,还高度集成,节省PCB空间,提高可靠性,是一个不可多得多功能充电IC。

最后感谢MPS和EEWORLD组织的这次活动,希望以后举办更多类似的活动,让广大工程师了解新技术,有更多方案可以选择。

2 个赞

感谢分享 :+1: :smiling_face_with_three_hearts: