前言

前面的文章介绍了在单片机中常用的两种通信协议(USART和SPI),并给出了GD32F303对应的配置流程。这次介绍第三种常见的通信协议IIC。这此使用GD32的硬件IIC通信PCF8563实时时钟。

IIC

IIC,又名I2C,也是一种串口通信协议。其中包含一根时钟线一根数据线。

  • 首先,标准的IIC总线是需要外部上拉的,即时钟线和数据线外部同时接一个上拉电阻(常见10k)到供电(MCU供电电压3.3V或5V或其它供电电压))。对应的通信IO配置为开漏输出。这样做的目的很简单,当这组IIC总线上挂载多个设备时,可以接收从设备的应答信号数据接收。所以在空闲状态下,这两根线被外部上拉为高电平
  • 根据上面的基础,IIC定义了起始信号内容传输应答信号结束信号
  • 起始信号在时钟线在高电平的情况下,数据线由高电平被下拉成低电平。出现在每次通信的开始。
  • 结束信号是时钟线在高电平的情况下,数据线由低电平被上拉成高电平。出现在每次通信的结尾。
  • 内容传输是跟在起始信号之后的,这里分为7位、8位和10位传输。7位与8位可以理解成一个概念,8位无非是芯片厂家规格书里把最低位表示的读/写位也加了进去。发从设备地址时都是由固定的高7位数据加最后一位(写0,读1)组成。10位则是先发送高两位的地址和读写位,再发送低8位的地址。如0x06F0(0b-xxxx-x110-1111-0000),其实是发送了11位有效数据。本文介绍7(8)位地址传输
  • 应答信号就更好理解了,在一帧内容传输完后(8bit)的一个时钟通信周期内,从设备控制数据线是高电平还是低电平。低电平意味着从机收到刚刚传输的8bit内容,高电平代表着未收到(记得标准的IIC是开漏输出,外部上拉为高,通讯失败时默认就是高)。
  • 通讯速度。不同于SPI,SPI是只要主机能发送的通信频率够快,从机也能接受足够高的通信频率,理论上是可以无限大的。像USART有常见的通信频率有4800、9600、38400、115200等等。IIC标准速率为100kbps,也有快速模式400kbps和高速模式3.4Mbps。在GD32F303中支持标准速率为100kbps,快速模式400kbps和快速+模式1Mbps

由于IIC通信速率的限制和较为完整的通信协议,通常用于数据量不是非常大的场合。如从AT24CXX系列EEPROM芯片读取或写入几个或几十个数据、从PCF8563时钟芯片读取或写入实时时钟数据、利用IIC通讯一些特定的功能IC(如南芯的SC8812去实现PD协议充电,又如通信MPU6050获取各种姿态角数据等等)、又或者驱动0.96寸的OLED显示等等。本文则是使用GD32F303的硬件IIC去实现对PCF8563时钟的设置与读取时间

各模块程序编写

在配置前,请确保你已经有一个GD32F303包含其对应标准库的keil工程,工程可使用官方的例程或可按照GD32F303调试小记(零)之工程创建与编译创建。此外,强烈建议身边有个示波器逻辑分析仪,用于查看我们端口输出的通信波形。

一、时钟配置

  • 开启GPIO端口时钟、GPIO引脚复用时钟、AF时钟、IIC模块的时钟(注意我用的是IIC1模块)。
void SystemClock_Reconfig(void)
{/* Enable all peripherals clocks you need*/rcu_periph_clock_enable(RCU_GPIOA);rcu_periph_clock_enable(RCU_GPIOB);rcu_periph_clock_enable(RCU_GPIOC);rcu_periph_clock_enable(RCU_GPIOD);rcu_periph_clock_enable(RCU_DMA0);rcu_periph_clock_enable(RCU_DMA1);rcu_periph_clock_enable(RCU_I2C1);
//      rcu_periph_clock_enable(RCU_ADC0);
//      rcu_periph_clock_enable(RCU_ADC2);
//      rcu_periph_clock_enable(RCU_USART1);rcu_periph_clock_enable(RCU_USART2);rcu_periph_clock_enable(RCU_SPI2);/* Timer1,2,3,4,5,6,11,12,13 are hanged on APB1,* Timer0,7,8,9,10             are hanged on APB2*/rcu_periph_clock_enable(RCU_TIMER1);   rcu_periph_clock_enable(RCU_AF);

二、GPIO配置

  • 根据上图中手册中对IIC1引脚的描述,相关IO配置如下:
// IIC port and pins definition
#define IIC1_PORT                   GPIOB
#define IIC1_SCL_PIN                GPIO_PIN_10
#define IIC1_SDA_PIN                GPIO_PIN_11void GPIO_Init(void)
{/* 使用SW下载,不使用JTAG下载,管脚用作其它功能 */gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);/* demo board IIC1 I/O */gpio_init(IIC1_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, IIC1_SCL_PIN | IIC1_SDA_PIN); }

三、IIC配置

  • 配置IIC1,速率60kHz、快速模式和快速+模式下高低电平比(我们用的标准,这个随意)、使用IIC模式和IIC从机7位地址模式、使能IIC1应答并使能IIC1模块。
/* IIC通信中PCF8563芯片的地址 */
#define ADDRESS_PCF8563         ((uint8_t)0xA2)void IICx_Init(void)
{/* configure I2C1 clock */i2c_clock_config(I2C1,60000,I2C_DTCY_2);/* configure I2C1 address */i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, ADDRESS_PCF8563);/* enable I2C1 */i2c_enable(I2C1);/* enable acknowledge */i2c_ack_config(I2C1, I2C_ACK_ENABLE);//  /* enable I2C1 DMA */
//  i2c_dma_enable(I2C1, I2C_DMA_ON);
}

四、IIC写函数

  • 使用IIC与从设备通信的代码不是唯一不变的,这由从设备自身的内部寄存器寻址位数决定 (有的从设备除了寄存器本身还有页操作)
  • 我们先来看看PCF8563这个芯片的数据手册中对其内部寄存器的描述:

  • 重点关注上图中用红色框线圈出来的部分,他们是我们使用IIC写入和读取的寄存器,且每个寄存器并不都是用足了8位,我们这里作个宏定义:
/* PCF8563芯片状态控制寄存器的地址 * 00H~01H共2个8位寄存器:*/
#define ADDRESS_CTL_STATUS1 ((uint8_t)0x00)       //控制状态寄存器 1
#define ADDRESS_CTL_STATUS2 ((uint8_t)0x01)       //控制状态寄存器 2/* PCF8563芯片里时间和日期寄存器的地址 * 从02H~08H共七个8位寄存器依次包含:秒(0~59)、分(0~59)、时(0~23)、* 日(1~31)、周几(0~6)、月份(1~12)、年份(0~99)*/
#define ADDRESS_SECOND_RES  ((uint8_t)0x02)       //秒寄存器
#define ADDRESS_MINUTE_RES  ((uint8_t)0x03)       //分寄存器
#define ADDRESS_HOUR_RES    ((uint8_t)0x04)       //时寄存器
#define ADDRESS_DAY_RES     ((uint8_t)0x05)       //日期寄存器
#define ADDRESS_WEEKDAY_RES ((uint8_t)0x06)       //周几寄存器
#define ADDRESS_MONTH_RES   ((uint8_t)0x07)       //月份寄存器
#define ADDRESS_YEAR_RES    ((uint8_t)0x08)       //年份秒寄存器/* PCF8563芯片时间寄存器最大位数* 秒和分最多7Bits* 时和日期最多6Bits*/
#define BCD_MinAndSec       ((uint8_t)0x7F)               //取低7位
#define BCD_HourAndDay      ((uint8_t)0x3F)               //取低6位
#define BCD_Weekday         ((uint8_t)0x07)               //取低3位
#define BCD_Months          ((uint8_t)0x1F)               //取低5位
#define BCD_Years           ((uint8_t)0xFF)               //取低8位
#define BCD_Century         ((uint8_t)0x80)               //取第7位 month寄存器里的bit7
  • 接着我们看看数据手册中推荐的多字节写流程。
  • 主机发送起始信号、主机发送从设备地址并在最低位写0表明是写操作、主机发送从地址中要操作的寄存器首地址、等待从机应答、主机发送8bit数据、再等待从机应答、主机再发数据、再等待从机应答、多次应答与发送数据后主机再发送停止信号。注意加粗的字,我们下面写的逻辑也应如此。
  • 我们再看看GD32中对写操作的流程建议。
  • 那我们根据上面两个手册中的时序要求我们的写法如下:
  • DevAddress为从设备地址,MemAddress为从设备中要操作的寄存器,再然后才是我们真正想要写入的数据。为了保证IIC时序的正确,我们看到官方给的每一个信号操作后都有标志位,还是那句老话,在所有非必要的死循环里有超时跳出机制。
void IICx_Mem_Write(uint32_t i2c_periph,uint8_t DevAddress,uint8_t MemAddress,uint8_t* ndata,uint8_t size,uint32_t Timeout)
{uint32_t Timeout_t=0;uint8_t i=0;Timeout_t = Timeout;/* wait until I2C bus is idle */while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY)){if(Timeout_t > 0)  Timeout_t--;else              break;}/* send a start condition to I2C bus */i2c_start_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until SBSEND bit is set */while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND)){if(Timeout_t > 0)  Timeout_t--;else              break;}/* send slave address to I2C bus*/i2c_master_addressing(i2c_periph, DevAddress, I2C_TRANSMITTER);Timeout_t = Timeout;/* wait until ADDSEND bit is set*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND)){if(Timeout_t > 0)  Timeout_t--;else              break;}/* clear ADDSEND bit */i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND); /* send a data byte */i2c_data_transmit(i2c_periph,MemAddress);Timeout_t = Timeout;/* wait until the transmission data register is empty*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE)){if(Timeout_t > 0)  Timeout_t--;else              break;}for(i=0;i<size;i++){/* send a data byte */i2c_data_transmit(i2c_periph, (*ndata));Timeout_t = Timeout;/* wait until the transmission data register is empty*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE)){if(Timeout_t > 0)  Timeout_t--;else              break;}ndata++;}/* send a stop condition to I2C bus*/i2c_stop_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until stop condition generate */ while(I2C_CTL0(i2c_periph)&0x0200){if(Timeout_t > 0)   Timeout_t--;else              break;}
}

五、IIC读函数

  • 还是先看看PCF8563芯片手册中的读时序。
  • Fig 19的读时序为:主机发送起始信号、主机发送从设备地址并在最低位写0表明是写操作、主机发送从地址中要操作的寄存器首地址、等待从机应答、主机再次发送起始信号、主机发送从设备地址并在最低位写1表明是读操作、从机发送8bit数据、再等待主机应答、从机再发数据、再等待主机应答、多次从机发送数据与主机应答后主机不应答并发送停止信号
  • 这里推荐Fig 19的读时序,道理也很简单。从寄存器读数据,首先你得知道你读的是什么寄存器。否则即使数据读出来了,你也不知道是谁的数据。每次读之前要先指向第一个要读的寄存器。
  • 接着我们看看GD32中对读时序的操作流程。

  • 上述GD32给出了两个主机接收方案。方案A对应使用IIC接收中断,方案B对应不使用IIC接收中断。考虑到通信不是很频繁,这里咱们使用B方案,也就是堵塞查询接收。
  • DevAddress为从设备地址,MemAddress为从设备中要操作的寄存器。先指向我们要读取的寄存器,再进行多个字节的读取。注意我这里给出的是多字节读取读取字节数必须不少于3 (代码里有段 i==(size - 3) )。如是要单字节读取,读完一次后,直接发送停止信号即可,不用管是否应答。
void IICx_Mem_Read(uint32_t i2c_periph,uint8_t DevAddress,uint8_t MemAddress,uint8_t* ndata,uint8_t size,uint32_t Timeout)
{uint32_t Timeout_t=0;uint8_t i=0;/******************************************************//*    Send Slave address and Specified Register Address *//******************************************************/Timeout_t = Timeout;/* wait until I2C bus is idle */while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY)){if(Timeout_t > 0)   Timeout_t--;else              break;}/* send a start condition to I2C bus */i2c_start_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until SBSEND bit is set */while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND)){if(Timeout_t > 0)  Timeout_t--;else              break;}/* send slave address to I2C bus*/i2c_master_addressing(i2c_periph, DevAddress, I2C_TRANSMITTER);//I2C_RECEIVER       I2C_TRANSMITTERTimeout_t = Timeout;/* wait until ADDSEND bit is set*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND)){if(Timeout_t > 0)   Timeout_t--;else              break;}/* clear ADDSEND bit */i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND); /* send a data byte */i2c_data_transmit(i2c_periph,MemAddress);Timeout_t = Timeout;/* wait until the transmission data register is empty*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE)){if(Timeout_t > 0)  Timeout_t--;else              break;}    /* send a stop condition to I2C bus*/i2c_stop_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until stop condition generate */ while(I2C_CTL0(i2c_periph)&0x0200){if(Timeout_t > 0)  Timeout_t--;else              break;}    /* enable acknowledge */i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);/******************************************************//*     Send Slave address and Read Data              *//******************************************************/Timeout_t = Timeout;/* wait until I2C bus is idle */while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY)){if(Timeout_t > 0)   Timeout_t--;else              break;}/* send a start condition to I2C bus */i2c_start_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until SBSEND bit is set */while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND)){if(Timeout_t > 0)  Timeout_t--;else              break;}/* send slave address to I2C bus*/i2c_master_addressing(i2c_periph, DevAddress, I2C_RECEIVER);Timeout_t = Timeout;/* wait until ADDSEND bit is set*/while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND)){if(Timeout_t > 0) Timeout_t--;else              break;}/* clear ADDSEND bit */i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND); for(i=0;i<size;i++){if( i==(size - 3) ){Timeout_t = Timeout;/* wait until the second last data byte is received into the shift register */while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC)){if(Timeout_t > 0)  Timeout_t--;else              break;            }/* disable acknowledge */i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);}Timeout_t = Timeout;/* wait until the RBNE bit is set */while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE)){if(Timeout_t > 0)  Timeout_t--;else              break;            }/* read data from I2C_DATA */(*ndata) = i2c_data_receive(i2c_periph); ndata++;}/* send a stop condition to I2C bus*/i2c_stop_on_bus(i2c_periph);Timeout_t = Timeout;/* wait until stop condition generate */ while(I2C_CTL0(i2c_periph)&0x0200){if(Timeout_t > 0)   Timeout_t--;else              break;}    /* enable acknowledge */i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);
}

六、实时时钟数据的处理

  • 做完了上面的第五步,还差一个数据处理,我们看看时钟芯片手册中是怎么描述其数据存储的形式的:
  • 看到上图手册中红框部分,我们知道时间和日期寄存器中都是以BCD码的形式记录的,且是BCD码中的8421码。这意味着我们把数据发送前得把我们正常的十进制数转换成BCD码,读取到数据后再把数据转化成十进制数。

1.先定义一个结构体,其中包含我们所需要时分秒年月日和周几:

typedef struct
{struct{uint8_t Second;uint8_t Minute;uint8_t Hour;}time;struct{uint8_t weekday;uint8_t day;uint8_t month;uint8_t year;}date;} PCF8563_Info;extern PCF8563_Info RTC_Message;

2. 十进制数转化成BCD码函数:

static uint8_t RTC_BinToBcd(uint8_t BinValue)
{uint8_t cacheBuf = 0;while(BinValue >= 10){BinValue -= 10;cacheBuf += 1;}cacheBuf = (cacheBuf<<4) + BinValue;return (cacheBuf);
}

3. BCD码转化成十进制数函数:

static uint8_t RTC_BcdToBin(uint8_t BCDValue,uint8_t xRegister)
{uint8_t cacheBuf = 0;cacheBuf = ( BCDValue & (xRegister&0xF0) ) >> 4;cacheBuf = cacheBuf*10 + (BCDValue & (xRegister&0x0F)); return (cacheBuf);
}

4.写实时时钟函数:

void Write_To_PCF8563(PCF8563_Info* set_pcf8563_time)
{    static uint8_t temp_send_BCD[7]={0};*temp_send_BCD           = RTC_BinToBcd(set_pcf8563_time->time.Second);*(temp_send_BCD + 1)   = RTC_BinToBcd(set_pcf8563_time->time.Minute);*(temp_send_BCD + 2)   = RTC_BinToBcd(set_pcf8563_time->time.Hour);*(temp_send_BCD + 3) = RTC_BinToBcd(set_pcf8563_time->date.day);*(temp_send_BCD + 4)  = RTC_BinToBcd(set_pcf8563_time->date.weekday);*(temp_send_BCD + 5)  = RTC_BinToBcd(set_pcf8563_time->date.month);*(temp_send_BCD + 6)    = RTC_BinToBcd(set_pcf8563_time->date.year);IICx_Mem_Write(I2C1,ADDRESS_PCF8563,ADDRESS_SECOND_RES,temp_send_BCD,7,0xFFFFU);
}

5.读实时时钟函数:

PCF8563_Info Read_From_PCF8563(void)
{PCF8563_Info Readbuf={0};static uint8_t ReadFromPCF[7]={0};IICx_Mem_Read(I2C1,ADDRESS_PCF8563,ADDRESS_SECOND_RES,ReadFromPCF,7,0xFFFFU);Readbuf.time.Second     = RTC_BcdToBin(ReadFromPCF[0],BCD_MinAndSec);Readbuf.time.Minute  = RTC_BcdToBin(ReadFromPCF[1],BCD_MinAndSec);Readbuf.time.Hour        = RTC_BcdToBin(ReadFromPCF[2],BCD_HourAndDay);Readbuf.date.day        = RTC_BcdToBin(ReadFromPCF[3],BCD_HourAndDay);Readbuf.date.weekday    = RTC_BcdToBin(ReadFromPCF[4],BCD_Weekday);Readbuf.date.month     = RTC_BcdToBin(ReadFromPCF[5],BCD_Months);Readbuf.date.year       = RTC_BcdToBin(ReadFromPCF[6],BCD_Years);return (Readbuf);
}

七、主函数部分

1. 显示部分

  • 这里我在之前的SPI章节配置好了屏显,并移植进去了lvgl图形界面库。这个是lvgl的任务处理函数,这里主要就是刷屏的。
void TASK_LCD_REFRESH(void)
{lv_task_handler();
}

2. 任务函数

PCF8563_Info RTC_Message={0};void TASK_PCF8563(void)
{RTC_Message = Read_From_PCF8563();
}

3. 主函数

  • 这里定义一个PCF8563_Info 类型的结构体变量,为修改实时时钟的时基做好准备,比如这里设置为:2021年11月10日23点59分55秒,周五。
  • TMT是个时间片框架,源码见GITEE,这里我们设一个任务,每过1000ms,读取一次实时时钟里的数据。
  • lvgl是一个轻量级的图形界面库,让一般的32位单片机都能有一个很好的UI界面显示,B站演示视频很多,这里不多介绍,我会在后期的文章中介绍如何把lvgl移植进GD32里。
int main(void)
{    PCF8563_Info set_pcf8563={.time.Second = 55,.time.Minute    = 59,.time.Hour      = 23,.date.weekday   = 4,.date.day        = 10,.date.month     = 11,.date.year      = 21};SystemTick_Init();   SystemClock_Reconfig();    GPIO_Init();Timer1_Init();Timer3_Init();DMA_Init();USARTx_Init();SPIx_Init();IICx_Init();FWDGT_Init();  NVIC_Init();/* 时间片框架(可忽略) */TMT_Init();/* lvgl库初始化(可忽略) */lv_init(); lv_port_disp_init();     lv_port_indev_init(); /* TMT任务创建,这里知道LCD_REFRESH每10ms执行一次,其它1s执行一次即可 */TMT.Create(TASK_LCD_REFRESH,10);TMT.Create(TASK_PCF8563,1000);TMT.Create(TASK_FWDGT_RELOAD,1000);/* 此处为lvgl中的btn控件创建,能让屏上显示变量这里也忽略即可 */lv_obj_t * btn1;lv_obj_t * btn2;lv_obj_t * btn3;lv_obj_t * btn4;lv_obj_t * btn5;lv_obj_t * btn6;static lv_style_t style1;.../*======================*//* 这里把赋的初值传过去 */Write_To_PCF8563(&set_pcf8563);while(1){TMT.Run();lv_label_set_text_fmt(label_1,"hello C world");                                                                                                                                       lv_label_set_text_fmt(label_4,"date:20%02d-%02d-%02d", RTC_Message.date.year,RTC_Message.date.month,RTC_Message.date.day);lv_label_set_text_fmt(label_5,"time: %02d:%02d:%02d", RTC_Message.time.Hour,RTC_Message.time.Minute,RTC_Message.time.Second);lv_label_set_text_fmt(label_6,"weekday:%d", RTC_Message.date.weekday);}
}

八、结果演示

1. 实际效果

硬件IIC读取时间

也可点击此处查看效果视频连接

2. 驱动波形

- IIC完整波形

  • 完整波形如上,黄线为时钟线,蓝线为数据线。频率应为60kHz,这里由于由于缩小了,导致示波器算频率不正确。

    - IIC起始部分
    - 这里就好多了,频率约60k的样子,从左边看是不是先有起始信号第一个数据是不是0xA2(0b1010 0010),然后有一个时钟周期的应答,看看是不是被从机拉低了。然后第二个数据是不是秒寄存器0x02(0b0000 0010)接着又是一个从机应答。以此类推。

    - IIC停止部分
    - 结束这边也一样,看看最后是不是一个停止信号

九、总结

  • 至此,我们把单片机中最常见的三种通信(SPI和USART可参见我的其它文章)都用GD32自带的硬件模块实现了。其实还有单总线通讯(只用一根线),学习之初用的DS18B20以及红外遥控器里的信号接收都是这个,我们还可以自己定义一个单总线协议,规定起始信号是什么波形、停止信号是什么模型、高电平是什么波形、低电平又是什么波形。就像打暗号一样,只要双方约定好一定的规范,那么它就能形成协议,并且通讯成功。

!!!本文为欢喜6666在CSDN原创发布,复制或转载请注明出处:)!!!

GD32F303调试小记(三)之IIC(硬件IIC+PCF8563实时时钟)相关推荐

  1. GD32F303调试小记(二)之SPI(软件SPI、硬件SPI、硬件SPI+DMA)

    前言 目前有一个项目中用到了TFT-LCD,其驱动芯片为ILI9341.为更好的达到显示效果,在最终的代码中我们会使用单片机自带的硬件SPI+DMA模块(由于调试过程中SPI+DMA输出的波形没能驱屏 ...

  2. GD32F303调试小记(一)之USART(接收中断、接收空闲中断+DMA、发送DMA)

    前言 之前写了GD32F103调试小记(二)之USART(接收中断.接收空闲中断+DMA.发送DMA)一文.这次我们来看看GD32F303的USART是如何配置的,结合这两篇文章,相信大家GD32的U ...

  3. 基于stm32硬件IIC的oled显示

    一 stm32硬件IIC 硬件IIC特性架构 ①通讯引脚 查看对应开发板芯片的原理图可以查看对应IIC外设引脚的位置 这里代码采用的是stm32f103c8t6,硬件IIC的外设引脚为PB6 PB7, ...

  4. 【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  5. 【正点原子FPGA连载】第三十二章RTC实时时钟LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  6. 基于cubemx的硬件iic SHT30的iic调试

    一直对stm32的硬件iic敬而远之,好像说是有问题的,就一直用的模拟iic实现,今天用sht30试了一下,发现基于cubemx来配置iic还挺方便的.现做如下记录 首先是硬件平台:stm32f103 ...

  7. STM32 HAL 硬件IIC+DMA+简单图形库控制OLED

    目录 前言 一.建立工程 二.编写和移植 前期准备 驱动部分修改 三.使用和验证 结论 (2022年1月22日重制)本文主要是移植带简单图形库的程序,如果只是实现DMA控制,建议看[0.96寸 OLE ...

  8. 【国货之光】GD32E230F4使用硬件IIC+DMA读写24C04

    在很久很久以前,我就写过GD32E230替换STM32F031的帖子,主要介绍了USART和SPI的外设移植开发,当时IIC使用的是软件i2c,没有介绍的价值.在使用IIC时,大多数我们都是采用软件的 ...

  9. 以MPU6050为例的硬件IIC的使用

    很久以前听说过硬件IIC的"难用",也在其他单片机上尝试过调试硬件IIC,调"通"过,却很容易卡死,于是默默给硬件IIC贴上个"不稳定"的标 ...

最新文章

  1. 推荐阅读20101115
  2. WebService大讲堂之Axis2(9):编写Axis2模块(Module)
  3. 当鼠标滑过文本框自动选中输入框内容JS代码
  4. java接口可以接两个吗,接口可以在Java中扩展多个接口吗?
  5. 经历越多困难,你就变的更韧性
  6. MFC根据颜色数组自定义位图并显示到窗口
  7. linux内核编译指定驱动,linux内核编译,配置本机驱动
  8. matlab数字电路仿真,MATLAB环境下的数字电路仿真
  9. nginx代理 域名重定向
  10. 计算机配置xp系统,教你WinXP系统如何设置一台电脑两个显示器
  11. vue循环jq渲染网页页面
  12. linux 下的lsb_release -a 命令查询系统版本
  13. wxpython中表格顶角怎么设置_wxpython编程之 grid(数据表格) | 学步园
  14. linux prom命令解释,eeprom 执行boot PROM命令
  15. 几种数据源的配置方式
  16. uniapp 真机调试 click点击事件无效
  17. 还有谁!!!?谁是Uber下一个要颠覆的行业?
  18. SparkSQL 创建 DataFrame 的方式
  19. Nordic nRF52内部DCDC使能
  20. 转载:Network 之三 Ethernet(以太网)物理介质(10Base、100Base-T、100Base-TX等)介绍

热门文章

  1. Android手机号校验(包含166,199开头)
  2. 史上最简单的排序-桶排序
  3. 嫁给程序员原来还有这福利,难怪有些女孩子找对象专门找程序员
  4. 【词目】:勿谓言之不预也
  5. 喵帕斯之天才算数少女
  6. 拼多多买菜进击一线城市:上海开团首日,1公里开出11家自提点
  7. DS3231的使用(一)
  8. python变量与赋值教学视频_Python第一课:print()函数、变量与赋值
  9. where和which的区别【定于从句】
  10. [Dest0g3 misc] Pngenius,EasyEncode,知道js吗,StringTraffic,Easyword,Python_jail,codegame