基于STM32F411 HAL库的CH452驱动程序


前言

关于CH452驱动网上资料较多,但都比较早,和个人实际需要存在出入,经参考整理,调通软硬件驱动程序,现将其整理分享,以作记录,驱动采用两线IIC形式,CH452为共阴极接法。由于硬件IIC很容易出问题,推荐使用软件IIC驱动。基础配置通过STM32CUBE生成。


一、硬件IIC驱动

1.硬件IIC GPIO选择

选择IIC1作为硬件IIC,即PB9为IIC的SDA, PB8为IIC的SCL
选择PD2作为CH452复位引脚,PC11作为CH452中断引脚,PC10作为CH452片选引脚

在这里插入图片描述

2.硬件IIC配置

在这里插入图片描述
在这里插入图片描述

3.硬件GPIO配置

在这里插入图片描述

4.硬件中断配置

不要忘记打开中断
在这里插入图片描述

5.添加驱动代码

生成代码后,在i2c.c中添加如下代码
通过CH452_HARDWARE_IIC_Write函数传输数据
在中断回调函数HAL_GPIO_EXTI_Callback中读取按键键值


static uint8_t CS_ADDR[2] = {0x20, 0x30};

void CH452_HARDWARE_IIC_Write(uint8_t cs, uint8_t addr, uint8_t data)
{
	//CH452为7位地址,当其片选为0时,地址为0x20
	//此处左移一位是由于HAL底层驱动传输的时候会直接将addr最后一位置0或1控制方向,针对7位地址格式的CH452需要左移一位才是正确的地址
	uint16_t address = (CS_ADDR[cs] | addr) << 1;
	while(HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)address, &data, 1, 0xff) != HAL_OK)
	{
		if(HAL_I2C_GetError(&hi2c1) != HAL_OK)
		{
			Error_Handler();
		}
	}
}


uint8_t CH452_HARDWARE_IIC_Read(uint8_t cs)
{
	uint8_t data = 0;
	uint16_t address = (CS_ADDR[cs] | 0x07) << 1;
	
	while(HAL_I2C_Master_Receive(&hi2c1, (uint16_t)address, &data, 1, 0xff) != HAL_OK)
	{
		if(HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
		{
			Error_Handler();
		}		
	}
	
	return data;
}


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_11)
	{
		uint8_t key_value = CH452_HARDWARE_IIC_Read(0);
	}
}

void CH452Ctrl_Init(void)
{
	//CH452硬件初始化
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET);//CS 0
	HAL_Delay(1);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);//RESET CH452
	HAL_Delay(1);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
	HAL_Delay(1);
	
	//0x07-两线接口ACK命令,0xff-无意义
	CH452_HARDWARE_IIC_Write(0, 0x07, 0xff);
	HAL_Delay(1);
	//0x04-设定系统参数命令,0x03-允许显示驱动,启用键盘扫描
	CH452_HARDWARE_IIC_Write(0, 0x04, 0x03);
	HAL_Delay(1);
	
	uint8_t i;
	for(i = 0; i < 8; i++)
	{
		//DIG0~8清屏
		CH452_HARDWARE_IIC_Write(0, 0x08 + i, 0x00);
	}
}

二、软件IIC驱动

1.软件IIC GPIO选择

与硬件IIC相同
在这里插入图片描述

2.软件GPIO配置

在这里插入图片描述

3.软件中断配置

不要忘记打开中断
在这里插入图片描述

4.TIM10配置

TIM10作为us延时的定时器,以匹配CH452软件IIC时序

在这里插入图片描述

5.系统时钟配置

配置系统时钟100MHz
在这里插入图片描述

6.添加驱动代码

生成代码后,在tim.h添加us延时函数声明

void delay_us(uint32_t nus);

在tim.c添加us延时函数

void delay_us(uint32_t nus)
{
	uint16_t differ = 0xffff - nus - 5;

	HAL_TIM_Base_Start(&htim10);
	__HAL_TIM_SetCounter(&htim10, differ);

	while(differ < 0xffff - 5)
	{
		differ = __HAL_TIM_GetCounter(&htim10);
	}
	
	HAL_TIM_Base_Stop(&htim10);
} 

新建IIC.h,添加如下驱动代码

#ifndef __CH452_CTRL_H
#define __CH452_CTRL_H

#include <stdint.h>
#include "stm32f4xx_hal.h"
#include "tim.h"

#define CH452_CS_PIN 			GPIO_PIN_10
#define CH452_CS_PORT 			GPIOC
#define CH452_INT_PIN 			GPIO_PIN_11
#define CH452_INT_PORT 			GPIOC
#define CH452_INT_EXTI_IRQn 	EXTI15_10_IRQn
#define CH452_RST_PIN 			GPIO_PIN_2
#define CH452_RST_PORT 			GPIOD
#define CH452_SCL_PIN 			GPIO_PIN_8
#define CH452_SCL_PORT 			GPIOB
#define CH452_SDA_PIN 			GPIO_PIN_9
#define CH452_SDA_PORT 			GPIOB

#define SCL_SET HAL_GPIO_WritePin(CH452_SCL_PORT, CH452_SCL_PIN, GPIO_PIN_SET)
#define SCL_RST HAL_GPIO_WritePin(CH452_SCL_PORT, CH452_SCL_PIN, GPIO_PIN_RESET)
#define SDA_SET HAL_GPIO_WritePin(CH452_SDA_PORT, CH452_SDA_PIN, GPIO_PIN_SET)
#define SDA_RST HAL_GPIO_WritePin(CH452_SDA_PORT, CH452_SDA_PIN, GPIO_PIN_RESET)

void CH452Init(void);
void CH452_SOFTWARE_IIC_Write(uint8_t addr, uint8_t data);
uint8_t CH452_SOFTWARE_Read(uint8_t cs);

#endif

新建IIC.c文件,添加如下驱动代码,软件IIC驱动代码参考正点原子相关程序

#include "IIC.h"

static void SDA_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	
	GPIO_InitStruct.Pin = CH452_SDA_PIN;
	HAL_GPIO_Init(CH452_SDA_PORT, &GPIO_InitStruct);	
}

static void SDA_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 	GPIO_InitStruct.Pull = GPIO_PULLUP;
  	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;	
	
	GPIO_InitStruct.Pin = CH452_SDA_PIN;
	HAL_GPIO_Init(CH452_SDA_PORT, &GPIO_InitStruct);	
}

static void IIC_Start(void)
{
	SDA_OUT();
	
	SCL_SET;
	SDA_SET;
	delay_us(1);

	SDA_RST;
	delay_us(1);
	SCL_RST;	
	delay_us(1);
}

static void IIC_Stop(void)
{
	SDA_OUT();
	
	SCL_RST;
	SDA_RST;
	delay_us(1);
	SCL_SET;
	SDA_SET;	
	delay_us(1);
}

static uint8_t IIC_Wait_Ack(void)
{
	uint8_t waitTime = 0;
	SDA_IN();
	
	SCL_SET;	
	delay_us(1);
	
	while(HAL_GPIO_ReadPin(CH452_SDA_PORT, CH452_SDA_PIN))
	{
		waitTime++;
		if(waitTime > 250)
		{
			IIC_Stop();
			return 1;
		}
	}
	
	SCL_RST;
	return 0;
}

static void IIC_Ack(void)
{
	SCL_RST;
	SDA_OUT();
	
	SDA_RST;
	delay_us(1);
	SCL_SET;
	delay_us(1);
	SCL_RST;
}

static void IIC_NAck(void)
{
	SCL_RST;
	SDA_OUT();
	
	SDA_SET;
	delay_us(1);
	SCL_SET;
	delay_us(1);
	SCL_RST;
}

static void IIC_Send_Byte(uint8_t data)
{
	uint8_t t;
	SDA_OUT();

	SCL_RST;
	for(t = 0; t < 8; t++)
	{
		if((data & 0x80) >> 7)
		{
			SDA_SET;
		}
		else
		{
			SDA_RST;
		}
		data <<= 1;
		delay_us(1);
		SCL_SET;
		delay_us(1);
		SCL_RST;
		delay_us(1);
	}
}

static uint8_t IIC_Read_Byte(uint8_t ack)
{
	uint8_t i, receive = 0;
	SDA_IN();
	
	for(i = 0; i < 8; i++)
	{
		SCL_RST;
		delay_us(1);
		SCL_SET;
		delay_us(1);
		receive <<= 1;
		if(HAL_GPIO_ReadPin(CH452_SDA_PORT, CH452_SDA_PIN)) receive++;
		delay_us(1);
	}
	if(!ack)
		IIC_NAck();
	else
		IIC_Ack();
	
	return receive;
}

static const uint8_t CS_ADDR[2] = {0x20, 0x30};
void CH452_SOFTWARE_IIC_Write(uint8_t cs, uint8_t addr, uint8_t data)
{
	IIC_Start();
	
	IIC_Send_Byte((CS_ADDR[cs] | addr) << 1);
	
	SDA_SET;
	delay_us(1);
	SCL_SET;
	delay_us(1);
	SCL_RST;
	SDA_RST;
	
//	if(addr == 0x07)
//	{
//		IIC_Wait_Ack();	  
//	}	

	IIC_Send_Byte(data);
	
	IIC_Stop();
	delay_us(100);
}

uint8_t CH452_SOFTWARE_Read(uint8_t cs)
{
	IIC_Start();
	
	IIC_Send_Byte(((CS_ADDR[cs] | 0x07) << 1) | 0x01);
	
	SCL_SET;
	delay_us(1);
	SCL_RST;
	delay_us(1);
	
	uint8_t data;
	data = IIC_Read_Byte(0);
	return data;
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_11)
	{
		uint8_t key_value = CH452_SOFTWARE_Read(0);
	}
}

void CH452Init(void)
{
	HAL_GPIO_WritePin(CH452_CS_PORT, CH452_CS_PIN, GPIO_PIN_RESET);//CS 0
	delay_us(100);
	HAL_GPIO_WritePin(CH452_RST_PORT, CH452_RST_PIN, GPIO_PIN_SET);//RESET CH452
	delay_us(100);
	HAL_GPIO_WritePin(CH452_RST_PORT, CH452_RST_PIN, GPIO_PIN_RESET);
	delay_us(100);
	
	CH452_Write(0, 0x07, 0xff);//2 WIRE ACK
	CH452_Write(0, 0x04, 0x03);//DISP-ON KEYB-ON INTM-LOW
	
	uint8_t i;
	for(i = 0; i < 8; i++)
	{
		CH452_Write(0, 0x08 + i, 0x00);
	}
}

初始化CH452后通过CH452_SOFTWARE_IIC_Write传输显示数据,通过中断回调函数HAL_GPIO_EXTI_Callback读取键值。


总结

STM32硬件IIC控制CH452配置方便,但实际使用中较易出现错误,具体原因需要根据实际情况去调试发现;软件IIC控制时序更清晰,但使用的资源较多。推荐根据自己实际需要选择。

链接:硬件IIC驱动程序 提取码:rker
链接:软件IIC驱动程序 提取码:1rwe

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用STM32F103C8T6的HAL库驱动MAX30100模块的步骤: 1. 首先,需要在CubeMX中配置STM32F103C8T6的GPIO和I2C外设。在GPIO中,配置SDA和SCL引脚为开漏输出模式。在I2C外设中,配置I2C的时钟速率和地址。 2. 在代码中包含MAX30100的I2C地址和命令字定义。 ``` #define MAX30100_I2C_ADDRESS 0xAE #define MAX30100_FIFO_WR_PTR 0x04 #define MAX30100_FIFO_RD_PTR 0x06 #define #define MAX30100_FIFO_DATA 0x07 #define MAX30100_MODE_CONFIG 0x09 #define MAX30100_SPO2_CONFIG 0x0A #define MAX30100_LED_CONFIG 0x0C #define MAX30100_TEMP_INT 0x1F #define MAX30100_TEMP_FRAC 0x20 ``` 3. 初始化I2C外设和MAX30100模块。在初始化MAX30100模块时,需要配置模块的工作模式、采样速率、LED亮度和红外补偿系数等参数。 ``` I2C_HandleTypeDef hi2c1; void MAX30100_init(void){ uint8_t tx_buffer[2]; tx_buffer[0] = MAX30100_MODE_CONFIG; tx_buffer[1] = 0x03; HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, tx_buffer, 2, 100); tx_buffer[0] = MAX30100_SPO2_CONFIG; tx_buffer[1] = 0x47; HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, tx_buffer, 2, 100); tx_buffer[0] = MAX30100_LED_CONFIG; tx_buffer[1] = 0x1F; HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, tx_buffer, 2, 100); } ``` 4. 读取MAX30100模块采集到的数据。首先需要读取FIFO读指针和写指针,计算出FIFO中数据的个数,然后通过I2C读取FIFO中的数据。 ``` uint8_t readFIFO(uint32_t *pun_red_led, uint32_t *pun_ir_led){ uint8_t uch_i; uint8_t ach_i2c_data[6]; uint8_t uch_temp; uint8_t uch_finger_detected; uint8_t uch_samples_read; uint32_t un_temp; int16_t n_brightness_difference; *pun_red_led = 0; *pun_ir_led = 0; uch_samples_read = 0; uch_finger_detected = 0; HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, &MAX30100_FIFO_WR_PTR, 1, 100); HAL_I2C_Master_Receive(&hi2c1, MAX30100_I2C_ADDRESS, &uch_temp, 1, 100); HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, &MAX30100_FIFO_RD_PTR, 1, 100); HAL_I2C_Master_Receive(&hi2c1, MAX30100_I2C_ADDRESS, &uch_i, 1, 100); uch_samples_read = uch_i - uch_temp; if(uch_samples_read == 0){ return 0; } for(uch_i = 0; uch_i < uch_samples_read; uch_i++){ HAL_I2C_Master_Transmit(&hi2c1, MAX30100_I2C_ADDRESS, &MAX30100_FIFO_DATA, 1, 100); HAL_I2C_Master_Receive(&hi2c1, MAX30100_I2C_ADDRESS, ach_i2c_data, 6, 100); un_temp = (uint32_t)ach_i2c_data[0]; un_temp <<= 16; *pun_red_led += un_temp; un_temp = (uint32_t)ach_i2c_data[1]; un_temp <<= 8; *pun_red_led += un_temp; un_temp = (uint32_t)ach_i2c_data[2]; *pun_red_led += un_temp; un_temp = (uint32_t)ach_i2c_data[3]; un_temp <<= 16; *pun_ir_led += un_temp; un_temp = (uint32_t)ach_i2c_data[4]; un_temp <<= 8; *pun_ir_led += un_temp; un_temp = (uint32_t)ach_i2c_data[5]; *pun_ir_led += un_temp; } n_brightness_difference = (int16_t)(*pun_red_led >> 16) - (int16_t)(*pun_ir_led >> 16); if(n_brightness_difference < 0){ n_brightness_difference = -n_brightness_difference; } if(n_brightness_difference > 500){ uch_finger_detected = 1; } return uch_finger_detected; } ``` 通过以上步骤,就可以使用STM32F103C8T6的HAL库驱动MAX30100模块进行心率检测和血氧饱和度检测了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值