STM32F1 MCP23017 IIC扩展IO口驱动16路LED指示灯

3 篇文章 0 订阅
1 篇文章 0 订阅

MCP23017芯片是通过IIC来扩展IO口用的,看了datasheet后,了解到其功能之强大,能作为输入也可以作为输出。作为输入时,还可以通过中断来通知输入引脚电平变化。

本文主要描述MCP23017扩展IO时作为输出来驱动LED的过程、实现、及注意事项。

电路接线示意图:

A0/A/A2器件地址选择引脚都接入到GND,MCP23017器件的地址为0x40。

MCP23017器件有很多寄存器地址,首先要关注的是BANK的值默认是多少。

从datasheet给出的表格可以看出,BANK的值默认是0。(先关注BANK值的原因是,BANK=0和BANK=1的各个寄存器地址是不同的,寄存器的值设置的不对,就无法得到预期的效果)

我的目标是让MCP23017作为IO输出,从电路连接示意图可以看出,输出低电平时,对应的LED指示灯亮,输出高电平时,对应的LED指示灯灭。

IODIRA和IODIRB这两个寄存器默认值都是0xFF,查阅datasheet,可以知道默认为输入。

要驱动LED亮灭,剩下的就是给寄存器配置高低电平了,找到对应的寄存器GPIOA和GPIOB。

测试的代码:

bsp_mcp23017.c

#include "bsp_mcp23017.h"


void init_iic_mcp23017(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(MCP_RCC_I2C_PORT, ENABLE);

	GPIO_InitStructure.GPIO_Pin = MCP_I2C_SCL_PIN | MCP_I2C_SDA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  	
	GPIO_Init(MCP_GPIO_PORT_I2C, &GPIO_InitStructure);

	i2c_stop();
}


void i2c_delay(void)
{
	uint8_t i;
	for (i = 0; i < 10; i++);
}


void i2c_start(void)
{
	MCP_I2C_SDA_1();
	MCP_I2C_SCL_1();
	i2c_delay();
	MCP_I2C_SDA_0();
	i2c_delay();
	MCP_I2C_SCL_0();
	i2c_delay();
}


void i2c_stop(void)
{
	MCP_I2C_SDA_0();
	MCP_I2C_SCL_1();
	i2c_delay();
	MCP_I2C_SDA_1();
}

void i2c_sendbyte(uint8_t _ucByte)
{
	uint8_t i;

	for (i = 0; i < 8; i++)
	{		
		if (_ucByte & 0x80)
		{
			MCP_I2C_SDA_1();
		}
		else
		{
			MCP_I2C_SDA_0();
		}
		i2c_delay();
		MCP_I2C_SCL_1();
		i2c_delay();	
		MCP_I2C_SCL_0();
		if (i == 7)
		{
			 MCP_I2C_SDA_1();
		}
		_ucByte <<= 1;
		i2c_delay();
	}
}

uint8_t i2c_readbyte(void)
{
	uint8_t i;
	uint8_t value;

	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		MCP_I2C_SCL_1();
		i2c_delay();
		if (MCP_I2C_SDA_READ())
		{
			value++;
		}
		MCP_I2C_SCL_0();
		i2c_delay();
	}
	return value;
}

uint8_t i2c_waitack(void)
{
	uint8_t re;

	MCP_I2C_SDA_1();
	i2c_delay();
	MCP_I2C_SCL_1();
	i2c_delay();
	if (MCP_I2C_SDA_READ())
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	MCP_I2C_SCL_0();
	i2c_delay();
	return re;
}

void i2c_ack(void)
{
	MCP_I2C_SDA_0();
	i2c_delay();
	MCP_I2C_SCL_1();
	i2c_delay();
	MCP_I2C_SCL_0();
	i2c_delay();
	MCP_I2C_SDA_1();
}

void i2c_nack(void)
{
	MCP_I2C_SDA_1();
	i2c_delay();
	MCP_I2C_SCL_1();
	i2c_delay();
	MCP_I2C_SCL_0();
	i2c_delay();	
}

u8 fn_mcp23017_iodir(uint16_t ports)
{
	u8 ADDR_MCP23017 = 0x40;
	i2c_start();
	i2c_sendbyte(ADDR_MCP23017);
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_sendbyte(0x00);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_sendbyte(ports >> 8);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
//	i2c_sendbyte(0x01);
//	
//	if (i2c_waitack() != 0)
//	{
//		goto cmd_fail;
//	}
	
	i2c_sendbyte((uint8_t)ports);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_stop();
	return 0;
	
	cmd_fail:
	i2c_stop();
	return 1;
}

u8 fn_mcp23017_setports(uint16_t ports)//A high 8 bits; B low 8 bits
{
	u8 ADDR_MCP23017 = 0x40;
	i2c_start();
	i2c_sendbyte(ADDR_MCP23017);
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_sendbyte(0x12);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_sendbyte(ports >> 8);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
//	i2c_sendbyte(0x13);
//	
//	if (i2c_waitack() != 0)
//	{
//		goto cmd_fail;
//	}
	
	i2c_sendbyte(ports);
	
	if (i2c_waitack() != 0)
	{
		goto cmd_fail;
	}
	
	i2c_stop();
	return 0;
	
	cmd_fail:
	i2c_stop();
	return 1;
}

bsp_mcp23017.h

#ifndef __BSP_MCP23017_H
#define __BSP_MCP23017_H
#include "stm32f10x.h"

#define MCP_GPIO_PORT_I2C		GPIOB
#define MCP_RCC_I2C_PORT 		RCC_APB2Periph_GPIOB
#define MCP_I2C_SCL_PIN			GPIO_Pin_10
#define MCP_I2C_SDA_PIN			GPIO_Pin_11

#define MCP_I2C_SCL_1()  GPIO_SetBits(MCP_GPIO_PORT_I2C, MCP_I2C_SCL_PIN)		/* SCL = 1 */
#define MCP_I2C_SCL_0()  GPIO_ResetBits(MCP_GPIO_PORT_I2C, MCP_I2C_SCL_PIN)		/* SCL = 0 */

#define MCP_I2C_SDA_1()  GPIO_SetBits(MCP_GPIO_PORT_I2C, MCP_I2C_SDA_PIN)		/* SDA = 1 */
#define MCP_I2C_SDA_0()  GPIO_ResetBits(MCP_GPIO_PORT_I2C, MCP_I2C_SDA_PIN)		/* SDA = 0 */

#define MCP_I2C_SDA_READ()  GPIO_ReadInputDataBit(MCP_GPIO_PORT_I2C, MCP_I2C_SDA_PIN)

void init_iic_mcp23017(void);
void i2c_delay(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_sendbyte(uint8_t _ucByte);
uint8_t i2c_readbyte(void);
uint8_t i2c_waitack(void);
void i2c_ack(void);
void i2c_nack(void);

u8 fn_mcp23017_iodir(uint16_t ports);
u8 fn_mcp23017_setports(uint16_t ports);

#endif

main.c

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif

#ifndef __BSP_BEEP_H
#define __BSP_BEEP_H
#include "bsp_beep.h"
#endif

#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "bsp_led.h"
#endif

#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif

#include "bsp_mcp23017.h"

int main()
{
	
	init_beep();
	init_led();
	init_iic_mcp23017();
	
	fn_mcp23017_iodir(0);//0-output/1-input
	
	func_led2_on();
	func_led3_on();
	func_led4_on();
	
	func_beep_play();
	delay_ms(100);
	func_beep_stop();
	delay_ms(1000);
		
	
	for(;;)
	{
		func_led4_on();
		fn_mcp23017_setports(0xffff);
		delay_ms(1000);
		func_led4_off();
		fn_mcp23017_setports(0x0000);
		delay_ms(1000);
	}
	
}

测试的效果图(我只接了一个LED,然后依次将LED的杜邦线接入GPA0~GPA7然后 GPB0~GPB7)

LED一秒闪烁一次

最后,还有一个要注意的问题,datasheet描述了寄存器自增的情况

在BANK=0时适用,如果不喜欢这种方式,可以通过代码调整,通过i2c_stop()和i2c_start()再发一次器件地址,寄存器地址,和寄存器设置值。但是这个方法比较笨拙。

  • 9
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值