GPIO通信

前面已经提到了GPIO的输入检测以及输出控制,现在记录一下关于树莓派GPIO的通信功能,在嵌入式中I2C、SPI、UART是最常用的通信协议,通过这几种协议我们可以实现与很多主从设备的通讯,今天以使用I2C的0.96寸OLED屏幕为例,利用树莓派自带的I2C接口来驱动OLED屏幕显示我们想要显示的东西。
在这里先补充一点协议相关的知识点:
全双工:可以收数据也可以发数据 可以同时进行 -----------两个数据线
半双工:可以收数据也可以发数据 不可以同时进行------- 一根数据线
单工 :只能收数据或者只能发数据 ----------------------------一根数据线
在这里插入图片描述

串行 :数据一位一位的传输 ---------一根线传输数据
在这里插入图片描述
例如我们即将提到的I2C、UART。
并行 :数据一次全部传输 ---------多根线传输数据
在这里插入图片描述
例如智能车竞赛中的摄像头就是采用的并行通信。(以上图片来自此文
现场总线:可以远距离传输 can (10km) 485(1km) //差模信号
工业控制中常用。
板级总线:芯片之间通信----近距离 IIC SPI //共模信号
一般的元器件间的通信方式。
同步通信:通信双方使用同一个时钟源(时钟频率)
异步通信:通信双方使用自己的时钟源
有关通信的具体分类见此博文

I2C简介

I2C是利浦公司推出的双向二线制总线,SCL时钟线和SDA数据线,用于数据传输,按照上面提到的知识点分类I2C是串行半双工板级同步有线传输总线。
一条总线挂载多个IIC接口器件-----并行连接在IIC总线上。
有关一组I2C总线最多可以挂接多少个I2C器件以及I2C的详细介绍大家参考此博文
在这里插入图片描述
有关I2C的传输流程,之前看见过以为博主把整个流程和踢球做了个类比,我觉得很形象,这里链接分享给大家
在这里插入图片描述
有关I2C的详细知识大家参考上述博文了解就好,笔者在此不做分析了,下面进入主题:使用树莓派的I2C。

树莓派4B+0.96OLED(I2C协议)

查询接口

打开终端命令,输入gpio readall,回车,在返回的IO表中可以看见有SDA1、SCL1;SDA0,SCL0两组I2C接口,我们使用SCL1与SDA1这一组进行。
在这里插入图片描述

硬件连接

接线方式如下:

树莓派OLED
5VVCC
GNDGND
3脚(SDA1)SDA
5脚(SCL1)SCL

在这里插入图片描述

配置树莓派I2C接口

有界面的或者不习惯命令行的建议直接跳过下面这节,参照有界面的进行。

无界面或者想用命令行的

使用putty或者Xshell登录树莓派,在终端命令输入sudo raspi-config,回车
进入如下界面。
在这里插入图片描述
用键盘上下选择3.Interface Option,回车,利用键盘上下选择I5 I2C 回车;
在这里插入图片描述
继续回车;
在这里插入图片描述
再继续回车;
在这里插入图片描述
利用鼠标右键选择finish,回车,返回命令窗口;
在这里插入图片描述
输入sudo reboot,重启树莓派。
在这里插入图片描述

有界面

打开Raspberry Pi Configuration
在这里插入图片描述
按下图打开I2C.
在这里插入图片描述
然后选择注销;Reboot等待重启。
在这里插入图片描述

wiringPiI2c库

库简介

跟之前使用IO的输入输出一样,也需要安装对应的库才能实现功能。有关wiringPiI2c库的函数与STM32 I2C的常用函数对比如下:

wiringPiI2cSTM32
wiringPiI2CSetup (const int devId) ;外设nit(void);初始化外设
wiringPiI2CReadReg8 (int fd, int reg)iic_read_byte(void);读取一个字节的数据
wiringPiI2CWriteReg8 (int fd, int reg, int data)iic_send_byte(u8 data)//放送一个字节的数据

在这里插入图片描述
有关此库的详细描述参考此链接
可以发现wiringPiI2CSetup (const int devId)中有一个参数,结合上面的踢球类比和I2C的理论知识,我们知道这个参数就是从机的地址,在树莓派中可以通过i2detect命令查询到外设的地址,要使用该命令还需要先安装i2c-tool。

安装i2c-tool

打开终端命令输入:sudo apt-get install i2c-tools 回车,等待重新返回命令行即可。(需要保证自己的树莓派有网)
在这里插入图片描述

查询I2C外设的地址

在命令行输入: sudo i2cdetect -l 回车,出现如下显示,说明使用的是I2C1接口。
在这里插入图片描述
然后输入: sudo i2cdetect -y 1,回车,会出现下面的显示,这里的3C就是OLED的地址(此时树莓派I2C不挂接其他的器件),一般的0.96寸OLED屏幕的地址都是0x30。
在这里插入图片描述

编程实现效果

通过上面的对比,我们知道整个I2C的函数只是被替换成了树莓派的函数接口,我们将平时使用的I2C屏幕驱动程序中的对应函数替换即可实现效果了,具体的替换过程可以参考此文
打开Geany,输入以下代码(取模部分代码太长了,需要的去笔者资源下载0积分或者点赞收藏私信博主获取。)

// An highlighted block
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<assert.h>
#include<termios.h>
#include<string.h>
#include<sys/time.h>
#include<time.h>
#include<sys/types.h>
#include<errno.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <wiringPiI2C.h>
#include <unistd.h>
typedef unsigned          char uint8_t;
typedef uint8_t  u8;
#define OLED_CMD   0x00
#define OLED_DAT   0x01
int fd;                                 
unsigned char  yi[16]={"Angle of beam:"};
unsigned char  er[16]={"ming"};
unsigned char san[16]={"Distance:"};
unsigned char  si[16]={"okok"};
const unsigned char zi[];
const unsigned char picture1[];
const unsigned char picture2[];
const unsigned char picture3[];
const unsigned char picture4[];
const unsigned char picture5[];
const unsigned char picture6[];
const unsigned char picture7[];

//画点函数
void OLED_SetPos(unsigned char x, unsigned char y) 
{ 
   WriteCmd(0xb0+y);
   WriteCmd(((x&0xf0)>>4)|0x10);
   WriteCmd((x&0x0f)|0x01);
}
//BMP位图显示
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
   unsigned int j=0;
   unsigned char x,y;

 if(y%8==0)
   	y = 0;
 else
   	y += 1;
   for(y=y0;y<y1;y++)
   {
   	OLED_SetPos(x0,y);
   	for(x=x0;x<x1;x++)
   	{
   		 wiringPiI2CWriteReg8(fd,0x40,BMP[j++]);
   	}
   }
}


void init(void)//OLED初始化
{
   wiringPiSetup();
       fd=wiringPiI2CSetup(0x3c);//i2c?
       wiringPiI2CWriteReg8(fd,0x00,0xa1);//t?í??90xa0
       wiringPiI2CWriteReg8(fd,0x00,0xc8);//L?úí??90xc0
       wiringPiI2CWriteReg8(fd,0x00,0x8d);//A?5w?
       wiringPiI2CWriteReg8(fd,0x00,0x14);
       wiringPiI2CWriteReg8(fd,0x00,0xa6);//óí?>:90xa7
       wiringPiI2CWriteReg8(fd,0x00,0xaf);// >:
}

void qingping(void)//清屏
{
   char zt1,zt2;
   for(zt1=0;zt1<8;zt1++)
   {
   	wiringPiI2CWriteReg8(fd,0x00,0xb0+zt1);
           for(zt2=0;zt2<128;zt2++) wiringPiI2CWriteReg8(fd,0x40,0x00);
   }
}


void ascii(float Angle,float distance)//字符显示函数
{
   sprintf(er,"%f",Angle);  // float 0 char
   sprintf(si,"%f",distance); // double 0 char 
   int zt;
       char zt3,zt4;
   for(zt3=0;zt3<4;zt3++)
       {
   	wiringPiI2CWriteReg8(fd,0x00,0xb0+(zt3*2));
               for(zt4=0;zt4<16;zt4++)
               {
   		         for(zt=0;zt<8;zt++)
                       {
                       	if(zt3==0) wiringPiI2CWriteReg8(fd,0x40,zi[yi[zt4]*16+zt]);
                               else if(zt3==1)  wiringPiI2CWriteReg8(fd,0x40,zi[er[zt4]*16+zt]);
                               else if(zt3==2)  wiringPiI2CWriteReg8(fd,0x40,zi[san[zt4]*16+zt]);
                               else if(zt3==3)  wiringPiI2CWriteReg8(fd,0x40,zi[si[zt4]*16+zt]);
                       }
   	}
               wiringPiI2CWriteReg8(fd,0x00,0xb0+(zt3*2)+1);
               for(zt4=0;zt4<16;zt4++)
               {
               	for(zt=0;zt<8;zt++)
                       {
                       	if(zt3==0) wiringPiI2CWriteReg8(fd,0x40,zi[yi[zt4]*16+zt+8]);
                               else if(zt3==1)  wiringPiI2CWriteReg8(fd,0x40,zi[er[zt4]*16+zt+8]);
                               else if(zt3==2)  wiringPiI2CWriteReg8(fd,0x40,zi[san[zt4]*16+zt+8]);
                               else if(zt3==3)  wiringPiI2CWriteReg8(fd,0x40,zi[si[zt4]*16+zt+8]);
                       }
               }
   }
}


int main()
{
   float Angle = 2.98754546;
   float distance = 5.754644545;

   init();
   delay(10);
   qingping();//清屏
   delay(10);
   ascii(Angle,distance);//字符显示
   delay(10);
   	
   while(1)
   {
   	//qingping();
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture1);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture2);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture3);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture4);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture5);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture6);
   	delay(1);
   	OLED_DrawBMP(0,0,128,8,(unsigned char *)picture7);
   	delay(1);
   }
}

然后命名保存,笔者觉得之前的代码一直放桌面太凌乱了,所以自己新建了文件夹,把代码放在了文件夹内,大家根据自己需求来就行。
在这里插入图片描述
由于我修改了目录所以编译的时候也就变了
输入是:cd Desktop/my_program/c(切换到我保存文件的目录)回车
然后输入gcc -o display display.c -lwiringPi 编译和生成执行文件 回车
最后 sudo ./display 回车 运行就可以看见如下效果了 。
在这里插入图片描述
可能是由于I2C的速度不够,导致看起来很不舒服,后面打算换个SPI的屏幕再试试,有关取模方法大家参考这篇博文就行,也可以私聊笔者要文档教程。

Python实现

参考此文

总结

笔者是想放冰墩墩的,但是官方不让发和冰墩墩有关的,想看效果的可以去B站查看

树莓派4B入门学习笔记汇总

树莓派4B学习笔记——系统烧录及初次开机
树莓派4B学习笔记——点亮你的LED
树莓派4B学习笔记——IO输入检测
树莓派4B学习笔记——IO通信篇(I2C)
树莓派4B学习笔记——IO通信篇(SPI)
树莓派4B学习笔记——IO通信篇(1-Wire)
树莓派4B学习笔记——IO通信篇(UART)

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐