在树莓派Zero上使用C#+Mono驱动TM1637四位数码管
最近闲着无聊,买了个树莓派Zero,准备在上面跑.Net Core,来驱动各种传感器
就是上面这货。之前手上已经有一个树莓派3B+,但是介于3B+已经被我挂在路由器旁边当做服务器用,不是很方便拿来研究接口,于是就挑了一个便宜的Zero玩玩,事实证明,我想太天真了,我以为只要是Linux系统,就能安装.net Core,实际上呢,我整了一个晚上才不得不认识到一个事实:即便是.net Core也是认CPU架构的,Pi Zero用的ARMv6就是不支持,哎早知道在买之前多做做功课了,买一个树莓派4也是个不错的选择啊。
幸好苍天不负有心人,我找到了 另外一个能在Linux上面运行.net的途径,那就是在Linux上面安装一个Mono,然后.net通过Mono当做虚拟机运行,其实在原理上和.net core是差不多的,可是Mono在性能上比原生的.net core差了很多便是,不过我们只是用来跑外部模块,也不是很需要多高性能便是了。
好了,唠嗑正式结束,让我们开始正题吧
首先,我们需要在Linux上面配置Mono的程序,讲人话就是安装Mono,不过在安装之前,我们还需要更改源,毕竟树莓派自带的源别指望在国内有好的下载体验
sudo sed -i 's#://raspbian.raspberrypi.org#s://mirrors.tuna.tsinghua.edu.cn/raspbian#g' /etc/apt/sources.list
sudo sed -i 's#://archive.raspberrypi.org/debian#s://mirrors.tuna.tsinghua.edu.cn/raspberrypi#g' /etc/apt/sources.list.d/raspi.list
运行上述两条指令,把树莓派自带的源替换成清华源,这样安装Mono会快很多
sudo apt-get install mono-devel mono-complete mono-dbg
运行上面指令后,在树莓派Zero上就会自动安装配置完毕Mono环境了。
对了,为了方便调试,我们还需要配置SSH的远程root连接
sudo nano /etc/ssh/sshd_config
运行上述指令后
找到这一条,然后改成上图这样子后(其实也就去掉#,后面的参数改成yes罢了)
完事以后,按Ctrl+X,退出编辑并覆盖保存就行。
sudo service ssh --full-restart
最后我们运行上述指令重启SSH服务以后就能够以root权限登录树莓派了。
以上是树莓派的系统的配置过程。
接下来我们需要配置Visual Studio
首先我们新建一个项目,由于最新的Mono支持.net core,所以我们直接建立.net core 3框架的项目就行,而且甚至不需要拖家带口带上.net core那么多运行库就能直接在Mono虚拟机下跑,简直了...
然后,我们需要有一个扩展能够直接在PC上远程调试树莓派上的程序,因此
搜索Mono的调试插件,有很多个,功能都差不多,挑一个顺手的就行
安装好Mono调试插件以后
需要配置下Mono调试插件的设置
其实主要的无非就是这么几个,新建一个配置,输入IP、端口、用户名和密码,避免麻烦最好直接上root权限,反正自己用
然后每次调试的时候,点击通过SSH生成和调试
就能获得和本地调试一样的体验,不得不说,这个体验实在是太好了。
接下来是项目的
其实也就一点,在Nuget上面找一个第三方的库来调用GPIO接口就行,没别的了
Nuget下搜索Raspberry,下面的库基本上都是关于调用树莓派gpio的,随便挑一个便是
我这边选择了文档最为齐全的Unosquare.Raspberry.IO
下面两个是依赖项,尤其是WiringPi,是直接管理接口的主要库
好,以上是准备工作,下面的是具体实现
上面这张图,对应的就是树莓派Zero上,一共40个针脚的定义,其中,两个5V的接口可以直接当做电源输入或者输出用,GND是接地这个没啥好说的,我们主要看GPIO,这里有很多很多GPIO接口,这些接口才是负责信号输入以及输出使用,我们控制的主要也是这些接口。
然后我们这次的主角也上场了
注意看接线的颜色,其中CLK和DIO代表时钟信号和数据信号,虽说是时钟信号,其实是类似于发送命令的接口,因此都接GPIO,VCC是电源,这个没啥好说的,就是输入电源(注意看传感器的电压,如果电压过高会烧毁传感器,所以树莓派预留了两个3.3V的电压接口),GND是接地,随便找个接地的接口插上去就行。
根据照片所示,我使用了4,14,16,18号接口,其中16口接了时钟信号,18口接了数据信号
好了,线也接好了,环境也配置好了,我们正式开始编程阶段
Pi.Init<BootstrapWiringPi>();//初始化通信接口,分配内存空间等
var clkPin = Pi.Gpio[BcmPin.Gpio23];//引用16接口
var dataPin = Pi.Gpio[BcmPin.Gpio24];//引用18接口
clkPin.PinMode = GpioPinDriveMode.Output;//设置16接口模式为输出
dataPin.PinMode = GpioPinDriveMode.Output;//设置18接口模式为输出
上面代码是初始化阶段,反正刚开头照这个姿势填就行了,值得注意的是接口引用部分
你看,我明明CLK接口插的是树莓派16号物理接口,为啥这里引用的却是GPIO23呢,其实这个是编码方式的不同导致的,主要有以下两种
- BCM
编号侧重CPU寄存器,根据BCM2835的GPIO寄存器编号。
- wiringPi
编号侧重实现逻辑,把扩展GPIO端口从0开始编号,这种编号方便编程。
具体使用哪一种,需要看调用库用的哪一套,因为我用的这个库使用的是Bcm(引用的时候已经写明了BcmPin)所以查表得知,16接口对应的GPIO23,18接口对应的GPIO24
接口配置完毕以后我们就可以正式开始驱动四位数码管了
驱动数码管实际上是操控TM1637芯片,我们的操作规程需要满足TM1637芯片的特性,
其中最主要的特性是
//数据输入开始
void startDisp()
{
clkPin.Write(GpioPinValue.High);//CLK拉为高电平
dataPin.Write(GpioPinValue.High);//DIO拉为高电平
dataPin.Write(GpioPinValue.Low);//和上面那句指令一起就是DIO由高变低
clkPin.Write(GpioPinValue.Low);//然后CLK上的时钟信号拉低,DIO接口的数据允许改变,代表开始写入数据
}
上面这四句执行完后,就表示告诉TM1637芯片,我要开始写数据了,后面DIO接口的任何电位变化,都是我要写的数据,下面就是写数据的过程
//开始写入数据
void writeByte(byte input)
{
for (int i = ; i < ; i++)//每次写入一个byte,一共8bit
{
clkPin.Write(GpioPinValue.Low);//确保无误输入前再拉低一次时钟 ,代表开始写入数据
if ((input & 0x01) == )//判断每一位的高低电平
{
dataPin.Write(GpioPinValue.High);
}
else
{
dataPin.Write(GpioPinValue.Low);
}
input >>= ;//每写入完毕一次,移动一位
clkPin.Write(GpioPinValue.High);//每次输入完一位,就拉高一次时钟
}
//应答信号ACK,这里本来是用来判断DIO脚是否被自动拉低,代表上面写入的数据TM1637已经接受到了
//但是我这里闲麻烦,直接将CLK信号低高低的拉,让芯片直接执行下一步操作
clkPin.Write(GpioPinValue.Low);//先拉低
clkPin.Write(GpioPinValue.High);//需要判断D是否为低电平此期间C一直拉高
clkPin.Write(GpioPinValue.Low);//应答完毕以后拉低C
}
上面执行完以后,我们就已经向芯片发送了一个字节,也就是8位的数据,写完以后,我们还需要告知芯片,数据传输完毕了
//结束条件是CLK为高时,DIO由低电平变为高电平
void stopDisp()
{
clkPin.Write(GpioPinValue.Low);//先拉低CLK,代表允许DIO改变数据
dataPin.Write(GpioPinValue.Low);//拉低DIO
clkPin.Write(GpioPinValue.High);//CLK拉高,满足结束条件前半部分
dataPin.Write(GpioPinValue.High);//DIO由低变高,代表数据输入结束
}
好了,上面三段代码一起执行完毕,就表示一个完整的,信号从准备输入,开始输入,结束输入的过程,每次要输入1字节,都需要经过以上三个过程,因此我们将上面三个过程分别写成各自的方法,毕竟是需要经常调用的东西,而且基本上都不会变。以上的代码实现,都是基于TM1637规格书,就是上面的接口说明实现的。
下面开始介绍该怎么在数码管上显示出东西来
在开始之前,我们先仔细看看数码管是怎么一个样子的
// A
// ---
// F | | B
// -G-
// E | | C
// ---
// D
XGFEDCBA
00111111, // 0 //0x3f
00000110, // 1 //0x06
01011011, // 2 //0x5b
01001111, // 3 //0x4f
01100110, // 4 //0x66
01101101, // 5 //0x6d
01111101, // 6 //0x7d
00000111, // 7 //0x07
01111111, // 8 //0x7f
01101111, // 9 //0x6f
01110111, // A //0x77
01111100, // b //0x7C
00111001, // C //0X39
01011110, // d //0X5E
01111001, // E //0X79
01110001 // F //0X71
上图展示了数码管,一个字样的显示方式,跟我们写汉字一样,一共准备了7个笔画,我们想让哪个笔画亮起来,就让那个笔画的电平拉高就行,总的来说还是挺直观的,因为我们只有7个笔画,但是一个比特有8位,所有还有一位空置为低电平,如果有其他用处的话,可以补上()。于是我们把这些二进制,通过计算器换算成16进制的话就变成了0x3F样式的字节码
static byte[] Characters = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0~9,A,b,C,d,E,F
当然,也可以自己任意根据上面的描述,编写想要的走线图案,不一定非要按照0到9的数字或者字母定式来写。
//设置基本参数
startDisp();//开始写入指令
writeByte(0x40);//指定功能参数
stopDisp();//结束写入指令 //设置显示地址以及显示内容
startDisp();
writeByte(0xC0);//设置首地址,指向第一个字符
var Date = DateTime.Now.ToString("hhmm").ToCharArray();//获得当前日期,并表示为小时分钟
byte[] Characters = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };//0~9,A,b,C,d,E,F
for (int i = ; i <4; i++)//循环更改四个字符的显示,想更改数码管的显示,只要更改循环体内的操作就行
{
if (i != ) writeByte(Characters[Date[i] - ]);//从Characters数组根据索引获得字符显示的编码
else writeByte((byte)(Characters[Date[] - ] + 0x80));//第二个字符带有冒号,因此将第一位空置拉高
}
stopDisp(); //开始写入亮度
startDisp();
writeByte(0x8f);
stopDisp();
以上代码便是驱动数码管显示的完整代码,循环运行上述代码就能不断驱动数码管显示当前的时间,同时更改循环体内的writeByte()方法参数,就能实现不同字符的显示。startDisp();stopDisp(); writeByte();方法体,都在上面有完全的展示。
除去上述三个方法,
writeByte(0x40);//指定功能参数
writeByte(0xC0);//设置首地址,指向第一个字符
writeByte((byte)(Characters[Date[1] - 48] + 0x80));//显示 :符号,这三个参数需要单独讲一下。
writeByte(0x8f);//指定亮度
首先,上述代码的先后顺序不能变,一定是先指定功能参数,后指定显示位置,然后指定显示内容,最后指定显示亮度
而功能参数0x40写入进去有啥用呢
我们查阅TM1637的规格书可知
0x40翻译成二进制便是
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0
B7|B6|B5|B4|B3|B2|B1|B0
根据上述表格我们可以知道01000000(0X40)所代表的的意思就是
1:数据写到显示寄存器,也就是功能是显示
2:地址的增加模式是自+1
3:测试模式为普通模式
在此介绍一下前两种的区别
第一条的意思就是,这个芯片是支持按键响应和屏幕输出的,也就是说,如果B1置1则芯片功能是读取按钮 (虽然数位管上并没有任何按键),B1置0就是显示输出模式
第二条的意思就是,
for (int i = ; i <; i++)//循环更改四个字符的显示,想更改数码管的显示,只要更改循环体内的操作就行
{
writeByte(Characters[Date[i] - ]);//从Characters数组根据索引获得字符显示的编码
}
如果B2置0,功能为自动地址增加模式,那么循环体内每次循环写入一个字符以后,下一次循环光标位置就会移到下一个字符的位置,就和我们打字类似
那么如果B2置1,功能为固定地址模式的话,顾名思义,就是哪个位置显示什么字符串由我们决定。
那么显示代码就变成了
startDisp();
writeByte(0xC0);//第一个字符
writeByte(Characters[Date[] - ]);
stopDisp(); startDisp();
writeByte(0xC1);//第二个字符
writeByte(Characters[Date[] - ]);
stopDisp(); startDisp();
writeByte(0xC2);//第三个字符
writeByte(Characters[Date[] - ]);
stopDisp(); startDisp();
writeByte(0xC3);//第四个字符
writeByte(Characters[Date[] - ]);
stopDisp();
那么这个0xC0,0xC1,0xC2,0xC3哪里来的呢,同样查阅规格书可知
就是11000000,11000001,11000010,11000011,上述几个二进制转换成16进制便是C0,C1,C2,C3,当然,该芯片最多可支持显示6个字符
对了,中间这个 : 的符号,并不占用一个字符显示,这个符号归类到0xC1地址内,被当成了一个标点使用
XGFEDCBA
, // 0 //0x3f
还记得上面那张图吧,A~G,分别表示7个笔画,但是多了一位闲置的在这里就派上用场了,只要把0xC1,也就是第二个字符的位置最高位置1,变成10111111,那么这个符号变会显示出来
writeByte((byte)(Characters[Date[] - ] + 0x80));
代码上就是在原先的基础上加上0x80就可以了。
上面的步奏全部完成以后,其实只是把需要显示的数据存到芯片里面而已,芯片还没有输出任何数据给数码管,因为我们还什么都看不到
所以我们还需要再输入一次命令
writeByte(0x8f);//指定亮度并显示
那这个 0x8f又是哪里来的呢
这里同样有一张表格,B3表示开关,B0~B2表示脉冲宽带pwm,脉冲宽度越长,代表输出给数码管的时间越长,也就越亮,参照之前的方法,把对应的8位二进制转换成16进制填入进去就行,我想都看到这里了,应该没啥疑问的。
好,上述就是完整的教程,下面贴完整代码
private static void Main(string[] args)
{
Pi.Init<BootstrapWiringPi>();//初始化通信接口,分配内存空间等
var clkPin = Pi.Gpio[BcmPin.Gpio23];//引用16接口
var dataPin = Pi.Gpio[BcmPin.Gpio24];//引用18接口
clkPin.PinMode = GpioPinDriveMode.Output;//设置16接口模式为输出
dataPin.PinMode = GpioPinDriveMode.Output;//设置18接口模式为输出
clkPin.Write(GpioPinValue.Low);//初始化电平为低,可不加
dataPin.Write(GpioPinValue.Low);//初始化电平为低,可不加 void startDisp()
{
//数据输入开始
clkPin.Write(GpioPinValue.High);//CLK拉为高电平
dataPin.Write(GpioPinValue.High);//DIO拉为高电平
dataPin.Write(GpioPinValue.Low);//和上面那句指令一起就是DIO由高变低
clkPin.Write(GpioPinValue.Low);//然后CLK上的时钟信号拉低,DIO接口的数据允许改变,代表开始写入数据
}
void stopDisp()
{
//结束条件是CLK为高时,DIO由低电平变为高电平
clkPin.Write(GpioPinValue.Low);//先拉低CLK,代表允许DIO改变数据
dataPin.Write(GpioPinValue.Low);//拉低DIO
clkPin.Write(GpioPinValue.High);//CLK拉高,满足结束条件前半部分
dataPin.Write(GpioPinValue.High);//DIO由低变高,代表数据输入结束
}
void writeByte(byte input)
{
//开始写入数据
for (int i = ; i < ; i++)//每次写入一个byte,一共8bit
{
clkPin.Write(GpioPinValue.Low);//确保无误输入前再拉低一次时钟 ,代表开始写入数据
if ((input & 0x01) == )//判断每一位的高低电平
{
dataPin.Write(GpioPinValue.High);
}
else
{
dataPin.Write(GpioPinValue.Low);
}
input >>= ;//每写入完毕一次,移动一位
clkPin.Write(GpioPinValue.High);//每次输入完一位,就拉高一次时钟
}
//应答信号ACK,这里本来是用来判断DIO脚是否被自动拉低,代表上面写入的数据TM1637已经接受到了,
//但是我们这里闲麻烦,直接将CLK信号低高低的拉,让芯片直接执行下一步操作
clkPin.Write(GpioPinValue.Low);//先拉低
clkPin.Write(GpioPinValue.High);//需要判断D是否为低电平此期间C一直拉高
clkPin.Write(GpioPinValue.Low);//应答完毕以后拉低C
}
void Show()
{
//设置基本参数
startDisp();//开始写入指令
writeByte(0x40);//指定功能参数为自动增加
stopDisp();//结束写入指令 //设置显示地址以及显示内容
startDisp();
writeByte(0xC0);//设置首地址,指向第一个字符
var Date = DateTime.Now.ToString("hhmm").ToCharArray();//获得当前日期,并表示为小时分钟
byte[] Characters = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };//0~9,A,b,C,d,E,F
for (int i = ; i < Date.Length; i++)
{
if (i != ) writeByte(Characters[Date[i] - ]);//从Characters数组根据索引获得字符显示的编码
else writeByte((byte)(Characters[Date[] - ] + 0x80));//第二个字符带有冒号,因此将第一位空置拉高
} //开始写入亮度
startDisp();
writeByte(0x8f);
stopDisp();
}
while (true)
{
Show();
}
}
以上是字符地址自增加的代码
private static void Main(string[] args)
{
Pi.Init<BootstrapWiringPi>();//初始化通信接口,分配内存空间等
var clkPin = Pi.Gpio[BcmPin.Gpio23];//引用16接口
var dataPin = Pi.Gpio[BcmPin.Gpio24];//引用18接口
clkPin.PinMode = GpioPinDriveMode.Output;//设置16接口模式为输出
dataPin.PinMode = GpioPinDriveMode.Output;//设置18接口模式为输出
clkPin.Write(GpioPinValue.Low);//初始化电平为低,可不加
dataPin.Write(GpioPinValue.Low);//初始化电平为低,可不加 void startDisp()
{
//数据输入开始
clkPin.Write(GpioPinValue.High);//CLK拉为高电平
dataPin.Write(GpioPinValue.High);//DIO拉为高电平
dataPin.Write(GpioPinValue.Low);//和上面那句指令一起就是DIO由高变低
clkPin.Write(GpioPinValue.Low);//然后CLK上的时钟信号拉低,DIO接口的数据允许改变,代表开始写入数据
}
void stopDisp()
{
//结束条件是CLK为高时,DIO由低电平变为高电平
clkPin.Write(GpioPinValue.Low);//先拉低CLK,代表允许DIO改变数据
dataPin.Write(GpioPinValue.Low);//拉低DIO
clkPin.Write(GpioPinValue.High);//CLK拉高,满足结束条件前半部分
dataPin.Write(GpioPinValue.High);//DIO由低变高,代表数据输入结束
}
void writeByte(byte input)
{
//开始写入数据
for (int i = ; i < ; i++)//每次写入一个byte,一共8bit
{
clkPin.Write(GpioPinValue.Low);//确保无误输入前再拉低一次时钟 ,代表开始写入数据
if ((input & 0x01) == )//判断每一位的高低电平
{
dataPin.Write(GpioPinValue.High);
}
else
{
dataPin.Write(GpioPinValue.Low);
}
input >>= ;//每写入完毕一次,移动一位
clkPin.Write(GpioPinValue.High);//每次输入完一位,就拉高一次时钟
}
//应答信号ACK,这里本来是用来判断DIO脚是否被自动拉低,代表上面写入的数据TM1637已经接受到了,
//但是我们这里闲麻烦,直接将CLK信号低高低的拉,让芯片直接执行下一步操作
clkPin.Write(GpioPinValue.Low);//先拉低
clkPin.Write(GpioPinValue.High);//需要判断D是否为低电平此期间C一直拉高
clkPin.Write(GpioPinValue.Low);//应答完毕以后拉低C
}
void Show()
{
//设置基本参数
startDisp();//开始写入指令
writeByte(0x44);//指定功能参数为固定地址显示
stopDisp();//结束写入指令 var Date = DateTime.Now.ToString("hhmm").ToCharArray();//获得当前日期,并表示为小时分钟
byte[] Characters = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };//0~9,A,b,C,d,E,F
//设置显示地址以及显示内容
startDisp();
writeByte(0xC0);//第一个字符
writeByte(Characters[Date[] - ]);
stopDisp(); startDisp();
writeByte(0xC1);//第二个字符
writeByte((byte)(Characters[Date[] - ] + 0x80));//第二个字符带有冒号,因此将第一位空置拉高
stopDisp(); startDisp();
writeByte(0xC2);//第三个字符
writeByte(Characters[Date[] - ]);
stopDisp(); startDisp();
writeByte(0xC3);//第四个字符
writeByte(Characters[Date[] - ]);
stopDisp(); //开始写入亮度
startDisp();
writeByte(0x8f);
stopDisp();
}
while (true)
{
Show();
}
}
以上是固定字符显示代码
对于.net core的项目来说,如果想使用Mono运行,那么命令是
执行mono xxxx.dll的方式,如果是普通的.net4.0框架的程序才是mono xxxx.exe的方式
在树莓派Zero上使用C#+Mono驱动TM1637四位数码管的更多相关文章
- sruts2:单个文件上传,多个文件上传(属性驱动)
文件上传功能在Struts2中得到了很好的封装,主要使用fileUpload上传组件. 1. 单个文件上传 1.1 创建上传单个文件的JSP页面.显示提交结果的JSP页面 uploadTest1.js ...
- Raspberry Pi(树莓派)上从零开始构建Linux系统(简称PiLFS)(一)
一. 准备工作 1. 装有Linux宿主系统的树莓派主板,可参考 Raspberry Pi(树莓派)上安装Raspbian(无路由器,无显示器) 2. 参考网址:Linux From Scratch ...
- 在树莓派2上安装 Windows 10
微软在2015年4月29日发布了树莓派玩家期待已久的 Windows 10 物联网核心预览版(Windows 10 IoT Core Insider Preview Image for Raspber ...
- 在树莓派3B上安装node.js
本文主讲如何在树莓派3B上安装node.js 环境描述1. 树莓派安装了`2016-11-25-raspbian-jessie-lite`(PS:在此版本的镜像中,默认禁用了ssh,在烧录好镜像之后, ...
- 2019 年在 Raspberry Pi 「树莓派」上运行的 10 个操作系统推荐
原文:2019 年在 Raspberry Pi 「树莓派」上运行的 10 个操作系统推荐 image Raspberry Pi** 是一款基于 ARM 的单板计算机,默认运行一款称为 Raspbian ...
- 【Linux开发】【CUDA开发】Ubuntu上安装NVIDIA显卡驱动
机型为戴尔Vostro3900 显卡型号为GTX 745 对于Nvidia显卡的驱动,如今很多Linux发行版会默认使用名为nouveau的驱动程序.Nouveau是由第三方为Nvidia开发的一 ...
- [七月挑选]树莓派Raspberrypi上配置Git
title: 树莓派Raspberrypi上配置Git 树莓派Raspberrypi上配置Git. 开始 首先你得有一树莓派!!! 过程 查看自己树莓派的版本 pi@raspberrypi:~ $ u ...
- 在香蕉派的树莓派系统上配置 Syncthing 自启动
在香蕉派的树莓派系统上配置 Syncthing 自启动 —— 魏刘宏 2020 年 1 月 19 日 首先做个名词解释,” 香蕉派” 是国内一款山寨树莓派的硬件产品,” 树莓派系统” 指的是”rasp ...
- raspberrypi(树莓派)上安装mono和jexus,运行asp.net程序
参考网址: http://www.linuxdot.net/ http://www.cnblogs.com/mayswind/p/3279380.html http://www.raspberrypi ...
随机推荐
- LeetCode75----分类颜色(变相快排)
给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 此题中,我们使用整数 0. 1 和 2 分别表示红色.白色和蓝色. ...
- 基于Xposed hook 实时监测微信消息
本文以微信版本6.7.3为例进行分析有hook, 大部分做微信机器人的话,首先要实时抓取微信的消息,在这里展示三种方式对微信的消息进行hook: 1.基于UI层拉取加载进行监听 2.基于微信dao层调 ...
- LeetCode 43. 字符串相乘(Multiply Strings) 大数乘法
题目描述 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 示例 1: 输入: num1 = "2" ...
- Windows下安装TensorFlow教程
目录 安装Python3.6 配置环境变量 安装TensorFlow 验证安装 报错或选版本 安装Python3.6 建议直接安装anaconda 下载地址:https://www.anaconda. ...
- webpack 第二部分
默认根目录 当前项目 修改目录 devServer devServer:{ open:true, //自动打开浏览器 port:3000, // 端口 contentBase:"dist&q ...
- out 传值(传址)
传值,只将这个变量的值给拿走,不返还,除非return赋值.将a的值传入函数,无论这个值在函数中如何变化,不会影响main中的a 传址,将这个变量的值拿走运算,完成后还是得返还回来(不用return, ...
- android studio中方法和类被调用多次,但是AS显示灰色,解决办法
Android Studio里面的一些类及方法,明明有被其他的类或者方法调用,但是去看的时候显示灰色,鼠标放上面的时候显示:Class ‘XXX’ is never used或者Method ‘XXX ...
- ListView 中如何优化图片?
图片的优化策略比较多.1.处理图片的方式:如果 ListView 中自定义的 Item 中有涉及到大量图片的,一定要对图片进行细心的处理,因为图片占的内存是ListView 项中最头疼的,处理图片的方 ...
- Computer Network Homework2’s hard question
Computer Network Homework2’s hard question 2. What is the signal which is used to modulate the origi ...
- 物料批量盘点,调用其中两个BAPI BAPI_MATPHYSINV_COUNT BAPI_MATPHYSINV_CHANGECOUNT
涉及两个BAPI:录入数量BAPI_MATPHYSINV_COUNT 修改数量:BAPI_MATPHYSINV_CHANGECOUNT REPORT ZSC_133 NO STANDARD PAGE ...