45 STM32 IIC主机、从机通信实例(ma51t12b触摸按键芯片)
45.1引言
最近在研究触摸按键板的通信,按键板主要用到的通信协议为IIC,事实上IIC的例子有很多,但大都都是模拟IIC的主机(引用某网友的评论),从机这边的内容还是比较少。
早在两三年前就跟着原子哥的学习资料,学习并操作过IIC的eeprom器件,但是依葫芦画瓢,虽然看了IIC的协议,但是还不算很透!!!只能说熟悉。最近接到这个项目,用到IIC通信,原本很快就搞定了,但是遇到了一些问题,发现是从机地址写错了(感觉是芯片厂商故意在文档里面写错的),不过换算挺好的,借此机会算是把IIC较为透彻的跑一遍。
45.2 实验环境
本次IIC实验环境使用了两块stm32f103C8t6最小开发板,一个做IIC主机,一个做IIC从机,使用的是标准库完成。
硬件环境:
IIC主机:使用了模拟IIC;
IIC从机:使用STM32自带的标准库;
还有逻辑分析仪!!(因为IIC学会了使用这家伙,感觉还不错)
45.3 IIC知识
(0)IIC支持一主多从,属于半双工,
主机跟从机通信,由直接先发目标从机的地址,目标从机匹配好自己地址后会回复主机一个ACK,然后根据从机定义的读写协议进行通信。
(1)IIC总线在空闲状态SDA和SCL都是高电平;
(2)IIC的起始信号和结束信号;
在使用模拟IIC的时候,按照时序图来就好,延时us级延时,根据原子哥那个延时应该没啥问题。
(3)IIC应答信号
应答信号,是数据传输第8位完成后,第九位需要应答,如果SDA在第九个时钟被拉低,表示发生了应答信号,否则没有应答。
(4)数据时序
传输数据的时候,在时钟跳变的时候,SDA必须保持稳定,否则数据错乱。
(7)IIC主从交互的时候,从机如何判断主机是要写?还是要读呢?
本来以前我没怎么关注这个问题,因为一些外设器件已经设计好了,提供了相关的驱动demo,在用的时候不会深究每一个细节,但当这次用stm32模拟从机的时候,我就遇到了这个问题,因为我要读取从机的数据。
好了,从机如何判断主机要读?还是写?是基于主机发送从机地址的第1位,是高还是低,来判断的!!!如下图所示。
主机写时序:
主机读时序:
结合上面两张图,就可以看到就是从机地址的第1位,是否为0或1来判断的。
注:上述的IIC从机设备的地址只有高7个字节,所以第1位是用来判断,主机到底是要写还是要读的。如果地址是其它位数的,地址的第1位,作用也是一样的功能,就是用来判断是否读写!!
(6)IIC完整的数据时序示例
45.4 STM32F103C8T6 IIC模拟主机代码
IIC头文件代码:
#ifndef __MYIIC_H
#define __MYIIC_H#include "sys.h"
#include "stm32f10x.h"//IO操作函数
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA //IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
IIC源文件代码:
#include "myiic.h"
#include "delay.h"//
//初始化IIC
void IIC_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); // 使能PB端口时钟 //PB8-SCL PB9-SDAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; //选择对应的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PC端口GPIO_SetBits(GPIOB, GPIO_Pin_8); // 关闭所有LEDGPIO_SetBits(GPIOB, GPIO_Pin_9); // 关闭所有LED}//IO方向设置
void SDA_IN(void)
{GPIO_InitTypeDef GPIO_InitStructure;//PB9-SDAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //选择对应的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}void SDA_OUT(void)
{GPIO_InitTypeDef GPIO_InitStructure;//PB9-SDAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //选择对应的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}//产生IIC起始信号
void IIC_Start(void)
{SDA_OUT(); //sda线输出IIC_SDA=1; IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
} //产生IIC停止信号
void IIC_Stop(void)
{SDA_OUT();//sda线输出IIC_SCL=1;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号delay_us(4);
}//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{u8 ucErrTime=0;SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0 return 0;
} //产生ACK应答
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}//不产生ACK应答
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{ u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){ IIC_SDA=(txd&0x80)>>7;txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(2); IIC_SCL=0; delay_us(2);}
}//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++; delay_us(1); } if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACK return receive;
}
45.5 STM32F103C8T6 IIC硬件从机代码
说到IIC的从机代码,网上找了挺多的,但好像标准库的比较少,导致要自己翻了 较多的博客才搞定,现在共享出来,为了大伙更好的学习吧。
IIC头文件代码:
#ifndef __MYIIC_H
#define __MYIIC_H#include "sys.h"
#include "stm32f10x.h"void I2C1_Init(void);#endif
IIC源文件代码:
#include <stdio.h>
#include <stdint.h>
#include "myiic.h"
#include "delay.h"/*---------IIC1---------------*/
u8 I2C1_ADDRESS = 0x68; //7 位 I2C 地址vu8 I2C_Status = 0;vu8 I2C1_Buffer_Tx[8] = {0};
vu8 I2C1_Buffer_Rx[8] = {0};vu32 Tx_Counter = 0;
vu32 Rx_Counter = 0;
//void I2C1_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;I2C_InitTypeDef I2C_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);I2C_DeInit(I2C1);I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = I2C1_ADDRESS; //从机地址,一定要设置正确I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = 100000;I2C_Init(I2C1, &I2C_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;//事件中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;//错误中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);I2C_ITConfig(I2C1, I2C_IT_BUF | I2C_IT_EVT |I2C_IT_ERR, ENABLE); I2C_Cmd(I2C1, ENABLE); /*允许1字节1应答模式*/I2C_AcknowledgeConfig(I2C1, ENABLE); }// I2C1 作为从机,用于中断接收从机数据
void I2C1_EV_IRQHandler(void)
{uint8_t ch = 0;switch(I2C_GetLastEvent(I2C1)){// 收到匹配的地址数据case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:{I2C_Status = 1;//I2C_GenerateSTOP(I2C1, DISABLE);break;}// 收到一个字节数据case I2C_EVENT_SLAVE_BYTE_RECEIVED:{I2C_Status = 2;//I2C_ClearITPendingBit(I2C1, I2C_IT_RXNE); ch = I2C_ReceiveData(I2C1);I2C1_Buffer_Rx[2] = ch;break;}case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: //发送数据{I2C_Status = 3;I2C_SendData(I2C1, 0x77);//I2C_ClearITPendingBit(I2C1, I2C_IT_TXE); break;}//发送数据,要发送,不然锁死,不过 master 没收到case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:{I2C_Status = 4;I2C_SendData(I2C1, 0x66);//I2C_ClearITPendingBit(I2C1, I2C_IT_TXE); break;}case I2C_EVENT_SLAVE_STOP_DETECTED: //收到结束条件{I2C_Status = 5;//I2C_GenerateSTOP(I2C1, ENABLE); I2C_Cmd(I2C1, ENABLE); break;}default: break;}}void I2C1_ER_IRQHandler(void)
{/* Check on I2C1 AF flag and clear it */ if (I2C_GetITStatus(I2C1, I2C_IT_AF)) { I2C_ClearITPendingBit(I2C1, I2C_IT_AF); } /* Check on I2C1 AF flag and clear it */ if (I2C_GetITStatus(I2C1, I2C_IT_BERR)) { I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);}
}
45.6 ma51t12b触摸按键芯片IIC通信实例
本次实验刚好用到这块ma51t12b触摸芯片,这里要吐槽一下他们的datasheet,从机的地址跟文档写的是不一样的,搞的我浪费了一天时间找问题,最后他们联系上厂商,他们发了一个51单片机的IIC读取demo,也不是stm32的,然后一对比,发现地址居然跟文档写的不一样,真的好气啊。
不过现在搞定了就好了,按照下面的时序进行读写操作。
废话不多说,还是上代码把,因为我现在要吃午饭了。
ma51t12b.h 头文件
#ifndef __MA51T12B_H__
#define __MA51T12B_H__#define MA51T12B_DEVICE_ADDR 0xD0#define MA51T12B_MID_SENS_VALUE ((1<<7)|(8<<4)|(1<<3)|(8<<0))/register addr//
#define MA51T12B_CH1_2_SEN_ADDR 0x02
#define MA51T12B_CH3_4_SEN_ADDR 0x03
#define MA51T12B_CH5_6_SEN_ADDR 0x04
#define MA51T12B_CH7_8_SEN_ADDR 0x05
#define MA51T12B_CH9_10_SEN_ADDR 0x06
#define MA51T12B_CH11_12_SEN_ADDR 0x07#define MA51T12B_INT_MOD_ADDR 0x08
#define MA51T12B_SYS_CON_ADDR 0x09
#define MA51T12B_CH1_8_PD_ADDR 0x0C
#define MA51T12B_CH9_12_PD_ADDR 0x0D#define MA51T12B_CH1_4_OUT_ADDR 0x10
#define MA51T12B_CH5_8_OUT_ADDR 0x11
#define MA51T12B_CH9_12_OUT_ADDR 0x12
///void MA51TXX_WriteOneByte(uint8_t addr, uint8_t nByte);
uint8_t MA51TXX_ReadOneByte(uint8_t addr);void MA51TXX_Init(void);#endif
ma51t12b.c源文件
#include <stdio.h>
#include <stdint.h>
#include "sys.h"
#include "stm32f10x.h"
#include "ma51t12b.h"
#include "myiic.h"uint8_t keyNumValue = 0xff;
uint8_t keyASCIIValue = 'N';
uint8_t nCnt = 0;void MA51TXX_WriteOneByte(uint8_t addr, uint8_t nByte)
{uint8_t res = 0;//起始信号IIC_Start();//写从机地址IIC_Send_Byte(MA51T12B_DEVICE_ADDR);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}//写寄存器地址IIC_Send_Byte(addr);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}//写数据IIC_Send_Byte(nByte);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}//tIIC_Stop();delay_ms(10);
}uint8_t MA51TXX_ReadOneByte(uint8_t addr)
{uint8_t res = 0x0;//起始信号IIC_Start();//写从机地址IIC_Send_Byte(MA51T12B_DEVICE_ADDR);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}//写寄存器地址IIC_Send_Byte(addr);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}IIC_Stop();delay_us(50);//起始信号IIC_Start();//写从机地址IIC_Send_Byte(MA51T12B_DEVICE_ADDR | 0x01);//等待应答res = IIC_Wait_Ack();if(res ==1){printf("===>falied at (%d) line\r\n",__LINE__);return;}res = IIC_Read_Byte(0);IIC_Stop();delay_ms(10);return res;
}//初始化硬件
void MA51TXX_Init(void)
{ IIC_Init();delay_ms(500);//配置工作模式 0-正常模式 1-低功耗模式MA51TXX_WriteOneByte(MA51T12B_SYS_CON_ADDR,0x03);//配置中断输出-中灵敏度-低灵敏度值MA51TXX_WriteOneByte(MA51T12B_INT_MOD_ADDR,0x10);//设置按键灵敏度MA51TXX_WriteOneByte(MA51T12B_CH1_2_SEN_ADDR,MA51T12B_MID_SENS_VALUE);MA51TXX_WriteOneByte(MA51T12B_CH3_4_SEN_ADDR,MA51T12B_MID_SENS_VALUE);MA51TXX_WriteOneByte(MA51T12B_CH5_6_SEN_ADDR,MA51T12B_MID_SENS_VALUE);MA51TXX_WriteOneByte(MA51T12B_CH7_8_SEN_ADDR,MA51T12B_MID_SENS_VALUE);MA51TXX_WriteOneByte(MA51T12B_CH9_10_SEN_ADDR,MA51T12B_MID_SENS_VALUE);MA51TXX_WriteOneByte(MA51T12B_CH11_12_SEN_ADDR,MA51T12B_MID_SENS_VALUE);//使能1-12按键MA51TXX_WriteOneByte(MA51T12B_CH1_8_PD_ADDR,0x00);MA51TXX_WriteOneByte(MA51T12B_CH9_12_PD_ADDR,0x00);}
好了,到此结束了!通过本次实验,算是较为完整的熟悉了IIC,估计还差时序调节的问题吧。
45 STM32 IIC主机、从机通信实例(ma51t12b触摸按键芯片)相关推荐
- STM32的I2C主从机通信
最近一段时间在做I2C通信协议,需要在两块STM32之间做I2C通信,定的是主机用IO口模拟,从机用的是STM32的硬件I2C,我的项目要求是需要主从机之间能够进行一串数据收发而不出错,实验时在主机方 ...
- php上位机grps通信,松下PLC与上位机通信实例
[实例简介] 上位机采用c# 语言与下位机松下PLC进行串口通信的动态库文件,包括多字,单字或者位的读取和写入. [实例截图] [核心代码] PanasonicPlc └── PanasonicPlc ...
- stm8s开发(八) IIC的使用:IIC主机通信!
前面讲过两个常用的串口,UART和SPI,这次这次讲解一下另一个常用的串口:IIC(I2C)通信 科普IIC:一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL.所有接到IIC总线设备上 ...
- 【51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果)
一.设计目标 使用三个51单片机,实现多机通信. 二.实现功能 1号单片机可遥控2.3号单片机.超声波测距等: 1号单片机可集中显示步进电机的转速.转向.启停状态以及超声波测距结果. 三.硬件原理 5 ...
- stm32 IIC通信
1,物理层 1>IIC是一种两线串行的通信方式,SCL(时钟线),SDA(数据线) 2>IIC是一种高位先行的通信方式 3>IIC可以 ...
- STM32开发,串口和PC机通信(串口中断、FIFO机制),安富莱+正点原子程序合并
STM32开发,串口和PC机通信(串口中断.FIFO机制),安富莱+正点原子程序合并 1 概述 1.1 资源概述 1.2 实现功能 2 软件实现 2.1实现步骤 2.2 main()函数代码 2.3 ...
- STM32学习之旅④ USART串口和上位机通信
STM32系列博客: STM32学习之旅① 开发环境搭建 STM32学习之旅② 固件库的使用及工程模板的建立 STM32学习之旅③ 从点灯到代码移植 STM32学习之旅④ USART串口和上位机通信 ...
- STM32F103C6T6 | 模拟IIC主机读取AHT20温湿度传感器数据
关于STM32的IIC 一开始是用硬件内置的IIC来读取数据的,没有什么问题,但是不知道为什么后续断电上电之后一直没有数据,仿真看的话发现卡死在这些循环里面,我才反应过来网上一直说的硬件IIC有问题是 ...
- STM32驱动陀螺仪MPU6050的应用实例
STM32F407ZE 驱动陀螺仪MPU6050的应用实例,实现如下功能: ①使用MPU6050的驱动实现陀螺仪遥控 左倾:LED1亮 右倾:LED2亮 前倾:LED3亮 后倾:LED4亮 使用的是获 ...
最新文章
- Linux下用汇编输出Hello, world
- ubuntu20更换节点_Ubuntu 20.04 apt 更换国内源的实现方法
- 我自学python的路-Python学习路线图的总结
- 闲鱼有流量,毒具特色,“全面”的转转如何突围?
- 批量删除txt文档内容命令_Linux@实用操作命令
- (2)从实际项目谈起,基于MEF的插件框架之总体设计
- 训练日志 2019.1.16
- go 语言ase ecb加密_Go技术日报(20201112)
- 小程序日历插件的使用
- Windows Server 2008 多元密码策略配置
- .NET Windows服务开发流程
- Linux环境中的帮助命令有,Linux下的帮助命令
- 【转】android新建项目时 出现appcompat_v7工程错误和红色感叹号
- 【软件下载】Axure8.1正式版(含汉化包)
- 魔鬼训练Day1作业
- 用matlab机器人工具箱对SCARA机器人建模
- 锁定计算机和睡眠有什么区别,电脑的睡眠和休眠选项有什么区别?怎么唤醒休眠的电脑?...
- 网摘:漫谈law、equity、act、statute、code、bill等法律英语词汇
- 零基础学摄影nbsp;人像摄影调节光…
- 计算机常用的四种加密方法,电脑常见的几种加密方法
热门文章
- C++——set基本操作总结
- python爬虫防屏蔽_python爬虫程序如何预防被限制
- android相册幻灯片功能,玩机教程 篇四十五:「MIUI玩机技巧63」MIUI相册新增“幻灯片播放”功能...
- 知识百科:针式打印机打印头是核心技术
- 代扣代收 VS 代付代发
- android 自定义dns解析器,Android中DNS解析
- android 盈利模式
- python绘制随机网络图形
- Android UI系列之侧滑粘稠效果的实现
- html网页怎么播放无损,无损音质和标准音质有什么区别