if(da&0x80 == 0x80)
if((da&0x80) == 0x80)
坑死爹了,“==” 优先级比 “&” 高。。。。。。。。。
void IIC_Write_Byte(unsigned char IIC_Byte) {
unsigned char i;
for(i=0; i<8; i++) {
OLED_SCL_Clear(); // µ±SCLÏßΪµÍʱ£¬SDAÏßµÄ״̬²Å¿ÉÒԸıä
if((IIC_Byte&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_ByteµÄ¸ÃbitΪ1
} else {
OLED_SDA_Clear(); // IIC_ByteµÄ¸ÃbitΪ0
}
OLED_SCL_Set(); // µ±SCLÏßΪ¸ßʱ£¬SDAÏßµÄ״̬±»¶ÁÈ¡
OLED_SCL_Clear(); // ½«SCLÏßÇåÁ㣬Íê³ÉÕâÒ»¸öbitµÄ´«Êä
IIC_Byte = IIC_Byte << 1;
}
}
void IIC_Write_Byte(unsigned char IIC_Byte) {
unsigned char i, da;
da = IIC_Byte;
for(i=0; i<8; i++) {
OLED_SCL_Clear(); // µ±SCLÏßΪµÍʱ£¬SDAÏßµÄ״̬²Å¿ÉÒԸıä
if((da&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_ByteµÄ¸ÃbitΪ1
} else {
OLED_SDA_Clear(); // IIC_ByteµÄ¸ÃbitΪ0
}
OLED_SCL_Set(); // µ±SCLÏßΪ¸ßʱ£¬SDAÏßµÄ״̬±»¶ÁÈ¡
OLED_SCL_Clear(); // ½«SCLÏßÇåÁ㣬Íê³ÉÕâÒ»¸öbitµÄ´«Êä
da = da << 1;
}
} 垃圾keil一堆乱码
第一段代码是错误的,第二段代码是正确的。
形参只能读取,不能修改????
错了错了,两段代码都对,问题出在 ”==“ 和 ”&“ 优先级上。
..\HARDWARE\delay\delay.h(21): error C141: syntax error near 'void', expected ';'
报这种错误,肯定是报错的地方上面 忘加“ ;”,但是这一次这个错误没能立马解决,原因是:报错上方是引入头文件,那么就没想到是没加分号这个原因。
数组定义老是忘记加分号。。。。
unsigned char code Picture1[] = {
0x00,0x00,0x00,0x00
}
unsigned char code Picture1[] = {
0x00,0x00,0x00,0x00
};
上面是错的,下面是对的。
头文件引用导致重复定义的问题, 以及再消除重复定义时导致的未定义问题。
重复定义发生在,在头文件中定义变量:如管脚定义,常量定义等。
比如:
main.c 中需要使用 unsigned char code Picture1[][128]
那么就直接在 main.c 中#include "oledbmp.h" #include "oledfont.h"
但是 main.c 中还#include "oled.h"
那么 oled.h 中就不能#include "oledbmp.h" #include "oledfont.h"
而且 oled.c 中也不可以
原因是什么呢????不知道
(管脚定义冲突时应该更恶心)
时序问题
本来打算的是采用 “管前不顾后” 的策略,即前包含后不包含,前面拉低SCL,写完数据后,后面就不拉低了,发现不行。。。。感觉没问题啊。。。。
void IIC_Write_Byte(unsigned char IIC_Byte) {
unsigned char i;
for(i=0; i<8; i++) {
OLED_SCL_Clear(); // 当SCL线为低时,SDA线的状态才可以改变
if((IIC_Byte&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_Byte的该bit为1
} else {
OLED_SDA_Clear(); // IIC_Byte的该bit为0
}
OLED_SCL_Set(); // 当SCL线为高时,SDA线的状态被读取
// OLED_SCL_Clear(); // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<
IIC_Byte = IIC_Byte << 1;
}
}
void IIC_Write_Byte(unsigned char IIC_Byte) {
unsigned char i;
for(i=0; i<8; i++) {
OLED_SCL_Clear(); // 当SCL线为低时,SDA线的状态才可以改变
if((IIC_Byte&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_Byte的该bit为1
} else {
OLED_SDA_Clear(); // IIC_Byte的该bit为0
}
OLED_SCL_Set(); // 当SCL线为高时,SDA线的状态被读取
OLED_SCL_Clear(); // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<
IIC_Byte = IIC_Byte << 1;
}
}
第一段代码是错误的,第二段代码是正确的。
发现 “顾后不顾前”,貌似也能行:
void IIC_Write_Byte(unsigned char IIC_Byte) {
unsigned char i;
OLED_SCL_Clear();//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
for(i=0; i<8; i++) {
// OLED_SCL_Clear(); // 当SCL线为低时,SDA线的状态才可以改变<<<<<<<<<<<<<<<<<<<<<<<
if((IIC_Byte&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_Byte的该bit为1
} else {
OLED_SDA_Clear(); // IIC_Byte的该bit为0
}
OLED_SCL_Set(); // 当SCL线为高时,SDA线的状态被读取
OLED_SCL_Clear(); // 将SCL线清零,完成这一个bit的传输
IIC_Byte = IIC_Byte << 1;
}
}
上面的代码运行正确。
我明白为什么了!!!!
什么 ”顾前不顾后“ ”顾后不顾前“ 简直就是小孩子的说法,一点都不理论,一点都不贴近器件。SSD1306读取数据(也就是SDA线上的状态)是在SCL下降沿上读取的,所以每写 1bit 数据(也就是改变一次SDA线的状态),就要在SCL上产生一个下降沿,所以:::::
if((IIC_Byte&0x80) == 0x80) {
OLED_SDA_Set(); // IIC_Byte的该bit为1
} else {
OLED_SDA_Clear(); // IIC_Byte的该bit为0
}
OLED_SCL_Set(); // 当SCL线为高时,SDA线的状态被读取<<<<<<<<<<<<<<<<<<<<<<<<<<<<
OLED_SCL_Clear(); // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<<
这两行要在操作SDA后面!
基于以上的探索,在代码中删删减减,渐渐的找出了,哪些语句是必要的,哪些不是必要的(甚至是没用的),在这个过程,渐渐对 IIC总线 有了更准确的了解。
关于Acknowledge 后天反馈:以下错了
void IIC_Wait_Ack() {
// 造个假的脉冲,告诉RECEIVER:TRANSMITTER已经接收到RECEIVER的acknowledge了
// OLED_SCL_Clear();
// OLED_SCL_Set();
// OLED_SCL_Clear();
}
哈哈哈,全删了问题也不大。补充:全删了有问题。。。真不知道那一次没有问题的情况是怎么发生的
又瞎几把搞了,还是得从原理出发!!!
为什么会有Acknowledge这个信号,就是为了告诉transmitter receiver是否收到了byte,注意Acknowledge这个信号是在SDA上的,为什么我们的程序操作的是SCL呢???因为这个IIC总线的SDA,SCL都是单片机产生的,现在单片机不关心从机是否收到数据,默认传输是可靠,所以产生一个不伴随任何收发任何判断程序的时钟脉冲,如果不产生这个脉冲,那么SSD1306不知道你没产生,它照常通过SDA向主机发送Acknowledge信号,但是同时主机也向从机发送byte,这样就发生了冲突,通信失败!
万事要从理论思考,这样知识才能上升一个层次。(但是同时也不否定瞎几把尝试的作用,为理解原理提供了原料,所以要认真对待瞎几把试的结果,能透彻的分析结果,发现矛盾,接近真理!)
我有发现一个神奇的事情,大小写不判断问题,头文件的函数声明和其他文件的对其引用,是要求大小写严格对应的,但是头文件的函数声明和其对应C文件的函数定义确实大小写通用的!!!!!!!
oled.c:
void oled_display_full_picture(unsigned char picture[][128]) { <<<<<<<<<<<<<<<<<<<<<<<<<<
unsigned char i, n;
for(i=0; i<8; i++) {
IIC_Write_Command(0xb0+i);
IIC_Write_Command(0x00);
IIC_Write_Command(0x10);
for(n=0; n<128; n++) {
IIC_Write_Data(picture[i][n]);
}
}
}
oled.h:
void OLED_DISPLAY_FULL_PICTURE(unsigned char picture[][128]); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
main.c:
OLED_DISPLAY_FULL_PICTURE(Picture2); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 汉字库16x10
unsigned char code Character[][2][10] = {
{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/
};
// 汉字库16x10
unsigned char code Character[][2][10] = {
{
{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/
},
};
main.c中
OLED_Display_Part_Picture(6, 48+2, 2, 10, Character[0]);
第一块代码不行,只能打印出 “航” 的上半部分;第二块代码可以。
受二维数组影响,第一维可以不加括号,比如:
a[][3] = {1, 2, 3, 4, 5, 6};
a[][3] = {{1, 2, 3}, {4, 5, 6}}
效果是一样的,为何到三维就不行了呢!
我好像知道了!!!!!!!!!!!
只有最低维可以不加括号,或者是没加括号时,优先填最低维,填满再升一维再填。
显然第一段代码错误的原因是,最低维加括号了,想省第二维的括号,但是C语言不知道啊,它发现括号层级不够,那么就先省第一维,所以成了这样:
unsigned char code Character[][2][10] = {
{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/
};
===========>>>>>>>>>>>>> 变成下面这样
unsigned char code Character[][2][10] = {
{
{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
{}
},
{
{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38},
{}
}
/*"航",0*/
};
所以只显示了汉字的上半部分,哈哈哈哈原因找到了
之前传参是这样的:
void oled_display_full_picture(unsigned char picture[][128]) {
可见最低维是写死的,但是有时候不能用128,但是函数的其他部分都是需要的,那么该怎么办??128是不能换成变量的,这个应该和C语言的编译原理相关的,定义时就要把所需空间声明,而不是调用时再说。
所以我搞出来个这玩意: 使用指针,就不用写死最低维了,维度以形参变量传入
void OLED_Display_Part_Picture(unsigned char Start_Page, unsigned char Start_Column,
unsigned char Delta_Page, unsigned char Delta_Column,
unsigned char Dimension, unsigned char * Picture) {
}
在单片机环境下 “开疆辟土” 太懵逼了,调试法宝printf都没有,所以我选择先去DEV-C++验证一下
#include "stdio.h"
int main() {
unsigned char Character[][2][2] = {
{
{1, 2},
{3, 4}
}
};
void function(unsigned char a, unsigned char b, unsigned char dimension, unsigned char * Picture) {
char i, n;
for (i = 0; i< a; i++) {
for (n = 0; n < b; n++) {
printf("(%d, %d) = %d\n", i, n, Picture[n]);
}
Picture += dimension;
}
}
unsigned char ** a = Character;
function(2, 2, 2, Character[0]);
return 0;
}
问题不大
再去Keil,发现警告,不管,继续,成功显示。警告如下:
main.c(16): warning C182: pointer to different objects
main.c(17): warning C182: pointer to different objects
main.c(18): warning C182: pointer to different objects
main.c(19): warning C182: pointer to different objects
main.c(20): warning C182: pointer to different objects
main.c(21): warning C182: pointer to different objects
我好像遇到了一个大问题,C51函数传参最多传3个。。。。。。
http://www.51hei.com/bbs/dpj-31890-1.html
又进坑了,C语言功底不够深厚啊。这个坑是关于ASCII码的
前32个ASCII码(0~31)是ASCII控制字符,是有字符解释的。单片机做屏幕显示字库时,不许考虑只管从32开始的打印字符就好。
所以字库中是从ASCII码为32的字符开始存的
// ASCII库
const unsigned char code ASCII_8x6[][6] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sp 32
0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,// ! 33
0x00, 0x00, 0x07, 0x00, 0x07, 0x00,// " 34
0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14,// #
0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12,// $
所以我们使用char字符型变量索引ASCII字库时,要给一个偏移量 ‘ ’(空格space)。
void OLED_Display_Char_8x6(char Char, unsigned char ASCII[][6]) {
unsigned char n;
for (n = 0; n < 6; n++) {
IIC_Write_Data(ASCII[Char - ' '][n]); <<<<<<<<<<<<<<<<<<<<<<<
}
}
啊啊啊啊啊啊,我又把自己坑死了,太菜了。。。。。
针对屏幕打印停不下来,呈滚动效果。这里需要严肃检讨!!!!!!!!!!!!!!!
出现这个问题,我最先不是仔细的思考问题的原因,而是自动把问题原因归结到自己写的,取数字每一位并显示的代码上。其实这个问题仔细一想,就是循环的问题,人总是把问题归结到自己最不熟的东西上,而不是认真根据现象分析原因,但是循环上的问题我也找了一大会。。。。。。。
这次是unsigned char坑了我。。。。
unsigned char i = 0; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
else {
for (i = num_bit; i>=0; i--) { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
OLED_Relative_Display_Char_8x6(num_bit_arr[i] + 48, ASCII);
}
}
i = 0 —> i- - —> i = 255 —> i >= 0
用完unsigned char,也就是全是正数了,这个就成死循环了。