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触摸按键芯片)相关推荐

  1. STM32的I2C主从机通信

    最近一段时间在做I2C通信协议,需要在两块STM32之间做I2C通信,定的是主机用IO口模拟,从机用的是STM32的硬件I2C,我的项目要求是需要主从机之间能够进行一串数据收发而不出错,实验时在主机方 ...

  2. php上位机grps通信,松下PLC与上位机通信实例

    [实例简介] 上位机采用c# 语言与下位机松下PLC进行串口通信的动态库文件,包括多字,单字或者位的读取和写入. [实例截图] [核心代码] PanasonicPlc └── PanasonicPlc ...

  3. stm8s开发(八) IIC的使用:IIC主机通信!

    前面讲过两个常用的串口,UART和SPI,这次这次讲解一下另一个常用的串口:IIC(I2C)通信 科普IIC:一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL.所有接到IIC总线设备上 ...

  4. 【51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果)

    一.设计目标 使用三个51单片机,实现多机通信. 二.实现功能 1号单片机可遥控2.3号单片机.超声波测距等: 1号单片机可集中显示步进电机的转速.转向.启停状态以及超声波测距结果. 三.硬件原理 5 ...

  5. stm32 IIC通信

    1,物理层         1>IIC是一种两线串行的通信方式,SCL(时钟线),SDA(数据线)         2>IIC是一种高位先行的通信方式         3>IIC可以 ...

  6. STM32开发,串口和PC机通信(串口中断、FIFO机制),安富莱+正点原子程序合并

    STM32开发,串口和PC机通信(串口中断.FIFO机制),安富莱+正点原子程序合并 1 概述 1.1 资源概述 1.2 实现功能 2 软件实现 2.1实现步骤 2.2 main()函数代码 2.3 ...

  7. STM32学习之旅④ USART串口和上位机通信

    STM32系列博客: STM32学习之旅① 开发环境搭建 STM32学习之旅② 固件库的使用及工程模板的建立 STM32学习之旅③ 从点灯到代码移植 STM32学习之旅④ USART串口和上位机通信 ...

  8. STM32F103C6T6 | 模拟IIC主机读取AHT20温湿度传感器数据

    关于STM32的IIC 一开始是用硬件内置的IIC来读取数据的,没有什么问题,但是不知道为什么后续断电上电之后一直没有数据,仿真看的话发现卡死在这些循环里面,我才反应过来网上一直说的硬件IIC有问题是 ...

  9. STM32驱动陀螺仪MPU6050的应用实例

    STM32F407ZE 驱动陀螺仪MPU6050的应用实例,实现如下功能: ①使用MPU6050的驱动实现陀螺仪遥控 左倾:LED1亮 右倾:LED2亮 前倾:LED3亮 后倾:LED4亮 使用的是获 ...

最新文章

  1. Linux下用汇编输出Hello, world
  2. ubuntu20更换节点_Ubuntu 20.04 apt 更换国内源的实现方法
  3. 我自学python的路-Python学习路线图的总结
  4. 闲鱼有流量,毒具特色,“全面”的转转如何突围?
  5. 批量删除txt文档内容命令_Linux@实用操作命令
  6. (2)从实际项目谈起,基于MEF的插件框架之总体设计
  7. 训练日志 2019.1.16
  8. go 语言ase ecb加密_Go技术日报(20201112)
  9. 小程序日历插件的使用
  10. Windows Server 2008 多元密码策略配置
  11. .NET Windows服务开发流程
  12. Linux环境中的帮助命令有,Linux下的帮助命令
  13. 【转】android新建项目时 出现appcompat_v7工程错误和红色感叹号
  14. 【软件下载】Axure8.1正式版(含汉化包)
  15. 魔鬼训练Day1作业
  16. 用matlab机器人工具箱对SCARA机器人建模
  17. 锁定计算机和睡眠有什么区别,电脑的睡眠和休眠选项有什么区别?怎么唤醒休眠的电脑?...
  18. 网摘:漫谈law、equity、act、statute、code、bill等法律英语词汇
  19. 零基础学摄影nbsp;人像摄影调节光…
  20. 计算机常用的四种加密方法,电脑常见的几种加密方法

热门文章

  1. C++——set基本操作总结
  2. python爬虫防屏蔽_python爬虫程序如何预防被限制
  3. android相册幻灯片功能,玩机教程 篇四十五:「MIUI玩机技巧63」MIUI相册新增“幻灯片播放”功能...
  4. 知识百科:针式打印机打印头是核心技术
  5. 代扣代收 VS 代付代发
  6. android 自定义dns解析器,Android中DNS解析
  7. android 盈利模式
  8. python绘制随机网络图形
  9. Android UI系列之侧滑粘稠效果的实现
  10. html网页怎么播放无损,无损音质和标准音质有什么区别