dm3730平台oled显示时钟——ssd1306驱动

时间:2022-06-16 23:46:20
转载请注明出处:http://blog.csdn.net/bhj1119/article/details/73330863
        最近在dm3730平台上增加了oled显示屏,用来显示时间。功能虽然简单,但在此过程中还是总结了一些知识和经验,拿出来分享一下,水平有限,欢迎指正。         一,oled驱动芯片 oled驱动芯片一般采用ssd1306,初始化命令如下,具体含义可以参考ssd1306芯片手册。 [plain] view plain copyprint?
  1. void ssd1306_init(void)  
  2. {  
  3.     gpio_set_value(65, 1);  
  4.     udelay(100);  
  5.     gpio_set_value(65, 0);  
  6.     mdelay(50);  
  7.     gpio_set_value(65, 1);  
  8.   
  9.     ssd1306_write_byte(0xae, SSD1306_CMD);//--turn off oled panel  
  10.     ssd1306_write_byte(0x00, SSD1306_CMD);//---set low column address  
  11.     ssd1306_write_byte(0x12, SSD1306_CMD);//---set high column address  
  12.     ssd1306_write_byte(0x40, SSD1306_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)  
  13.     ssd1306_write_byte(0xb0, SSD1306_CMD);  
  14.     ssd1306_write_byte(0x81, SSD1306_CMD);//--set contrast control register  
  15.     ssd1306_write_byte(0xff, SSD1306_CMD); // Set SEG Output Current Brightness  
  16.     ssd1306_write_byte(0xa1, SSD1306_CMD);//--Set SEG/Column Mapping    0xa0左右反置 0xa1正常  
  17.     ssd1306_write_byte(0xa6, SSD1306_CMD);//--set normal display  
  18.     ssd1306_write_byte(0xa8, SSD1306_CMD);//--set multiplex ratio(1 to 64)  
  19.     ssd1306_write_byte(0x2f, SSD1306_CMD);//--1/48 duty  
  20.     ssd1306_write_byte(0xc8, SSD1306_CMD);//Set COM/Row Scan Direction  0xc0上下反置 0xc8正常  
  21.     ssd1306_write_byte(0xd3, SSD1306_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)  
  22.     ssd1306_write_byte(0x00, SSD1306_CMD);//-not offset  
  23.     ssd1306_write_byte(0xd5, SSD1306_CMD);//--set display clock divide ratio/oscillator frequency  
  24.     ssd1306_write_byte(0x80, SSD1306_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec  
  25.     ssd1306_write_byte(0xd9, SSD1306_CMD);//--set pre-charge period  
  26.     ssd1306_write_byte(0x21, SSD1306_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock  
  27.     ssd1306_write_byte(0xda, SSD1306_CMD);//--set com pins hardware configuration  
  28.     ssd1306_write_byte(0x12, SSD1306_CMD);  
  29.     ssd1306_write_byte(0xdb, SSD1306_CMD);//--set vcomh  
  30.     ssd1306_write_byte(0x40, SSD1306_CMD);//Set VCOM Deselect Level  
  31. }  

[plain] view plain copyprint?
  1. void ssd1306_display_on(void)  
  2. {  
  3.     ssd1306_write_byte(0x8D, SSD1306_CMD);  
  4.     ssd1306_write_byte(0x14, SSD1306_CMD);  
  5.     ssd1306_write_byte(0xAF, SSD1306_CMD);  
  6. }  

这里重点理解一下下面这段文字 dm3730平台oled显示时钟——ssd1306驱动
B0~B7表示起始页地址 00~0f表示起始列地址的低4位 10~1f表示起始列地址的高4位
上文所述,页地址设为B2h,列地址低4位为03h,列地址高4位为10h,这就意味着开始列是PAGE2的SEG3,数据字节将写入到RAM的第三列。网上有人问:高四位的起始地址10h起什么作用呢?
既然低4位是00h~0fh,高4位是10h~1fh,那么一共有8位,可以表示2^8=256列(这么说,ssd1306最大支持256列)。 高位10h~1fh共16个数,低位00h~0fh也是16个数,那么 16*16=256,也就是说,高位将列分成16等份,低位再将每一小份再等分16份,这样一共是256份,也就是256列了。 还是不明白?直接上图: dm3730平台oled显示时钟——ssd1306驱动
dm3730平台oled显示时钟——ssd1306驱动
这样列地址低4位为03h,列地址高4位为10h,就可以这样表示:绿框为列起始地址 dm3730平台oled显示时钟——ssd1306驱动 dm3730平台oled显示时钟——ssd1306驱动
如果列地址低4位为0eh,高4位为11h,就可以这样表示:绿框为列起始地址 dm3730平台oled显示时钟——ssd1306驱动 dm3730平台oled显示时钟——ssd1306驱动
那么,页地址又是什么概念呢?可以简单的理解为:一页有8行,128*64的屏有64/8=8页,64*48的屏有48/8=6页。  二,显示算法 [plain] view plain copyprint?
  1. int oled_post_word(u8 x, u8 y, u8 w, u8 h, const u8 *word, u8 r)  
  2. {  
  3.     if (y > (SSD1306_HEIGHT-1) || x + w > SSD1306_WIDTH) {  
  4.         return -1;  
  5.     }  
  6.       
  7.     if (h < 8) {  
  8.         return -1;  
  9.     }  
  10.       
  11.     u8 i, j, tmp, tmp1;  
  12.     u8 i0 = y/8;  
  13.     u8 i1 = (y + h - 1)/8;  
  14.     u8 y_8 = y%8;  
  15.     u8 y_8_8 = (8 - y_8);  
  16.     u8 h_8 = h%8;  
  17.     u8 h_y_8 = (h_8 + y_8);  
  18.     u8 h_y_8_8 = 8 - (h_8 + y_8);  
  19.     u8 h_8__8 = (8 - h_8);  
  20.     u8 h_8__h_y_8 = h_8 - (h+y)%8;  
  21.   
  22.     if(y%8) {  
  23.         for(i = i0;i <= i1;++i) {  
  24.             for(j = 0;j < w;++j) {  
  25.                 if(i == i0) {                 //第一行  
  26.                     if(TRUE == r) {  
  27.                         tmp = ~(word[w*(i-i0)+j]);//(*word++);//word[8*(i-i0)+j];  
  28.                     } else {  
  29.                         tmp = word[w*(i-i0)+j];   //*word++;  
  30.                     }  
  31.                     tmp <<= y_8;//y%8;                                            //低位左移  
  32.                     lcd_screen_data[SSD1306_WIDTH*i + x + j] &= 0xFF>>y_8_8;//(8-y%8);      //取低位  
  33.                     lcd_screen_data[SSD1306_WIDTH*i + x + j] |= tmp;  
  34.                 }else if(i == i1 && h_y_8 <=8) {                               //(h%8 + y%8)最后一行  
  35.                     if(TRUE == r) {  
  36.                         tmp = ~(word[w*(i-i0-1)+j]);//(*word++);//word[8*(i-i0)+j];   
  37.                     } else {  
  38.                         tmp = word[w*(i-i0-1)+j];   //*word++;  
  39.                     }  
  40.   
  41.           //tmp &= 0xFF>>(8 - h%8);                                //取低有效位  
  42.           tmp >>= y_8_8;                                           //高位右移  
  43.   
  44.           if(TRUE == r) {  
  45.             tmp1 = ~(word[w*(i-i0)+j]);//(*word++);//word[8*(i-i0)+j];  
  46.           } else {  
  47.             tmp1 = word[w*(i-i0)+j];   //*word++;  
  48.           }  
  49.             
  50.           //tmp &= 0xFF>>(8 - h%8);                                //取低有效位  
  51.           tmp1 <<= y_8;//y%8;                                      //高位右移  
  52.           tmp |= tmp1;  
  53.           tmp &= 0xFF>>h_y_8_8;//(8 - (h%8 + y%8));  
  54.             
  55.           lcd_screen_data[SSD1306_WIDTH*i + x + j] &= 0xFF<<h_y_8;//((h%8 + y%8));     //删除低位  
  56.           lcd_screen_data[SSD1306_WIDTH*i + x + j] |= tmp;  
  57.   
  58.         } else if(i == i1 && h_y_8 > 8) {                                   //(h%8 + y%8)最后一行  
  59.   
  60.           if(TRUE == r) {  
  61.             tmp = ~(word[w*(i-i0-1)+j]);//(*word++);//word[8*(i-i0)+j];  
  62.           } else {  
  63.             tmp = word[w*(i-i0-1)+j];   //*word++;  
  64.           }  
  65.           tmp &= 0xFF>>h_8__8;//(8 - h%8);                                  //取低有效位  
  66.           tmp >>= h_8__h_y_8;//h%8-(h+y)%8;                                 //高位右移  
  67.             
  68.           lcd_screen_data[SSD1306_WIDTH*i + x + j] &= 0xFF<<(((h+y)%8));              //删除低位  
  69.           lcd_screen_data[SSD1306_WIDTH*i + x + j] |= tmp;  
  70.         } else {                                                           //中间行  
  71.           if(TRUE == r) {  
  72.             tmp = ~(word[w*(i-i0-1)+j]);//(*word++);//word[8*(i-i0)+j];  
  73.           } else {  
  74.             tmp = word[w*(i-i0-1)+j];   //*word++;  
  75.           }  
  76.             
  77.           //tmp &= 0xFF>>(8 - h%8);                                        //取低有效位  
  78.           tmp >>= y_8_8;//8-y%8;                                           //高位右移  
  79.   
  80.           if(TRUE == r) {  
  81.             tmp1 = ~(word[w*(i-i0)+j]);//(*word++);//word[8*(i-i0)+j];  
  82.           } else {  
  83.             tmp1 = word[w*(i-i0)+j];   //*word++;  
  84.           }  
  85.             
  86.           //tmp &= 0xFF>>(8 - h%8);                                       //取低有效位  
  87.           tmp1 <<= y_8;//y%8;                                             //高位右移  
  88.           tmp |= tmp1;  
  89.           lcd_screen_data[SSD1306_WIDTH*i + x + j] = tmp;  
  90.   
  91.         }  
  92.       }  
  93.     }  
  94.   } else {  
  95.   
  96.     for(i = i0;i <= i1;++i) {  
  97.       for(j = 0;j < w;++j) {  
  98.         if(TRUE == r) {  
  99.           tmp = ~(word[w*(i-i0)+j]);//(*word++);//word[8*(i-i0)+j];  
  100.         } else {  
  101.           tmp = word[w*(i-i0)+j];   //*word++;  
  102.         }  
  103.   
  104.         if (i0 !=i1 && i == i1) {                                   //最后一行  
  105.           tmp &= 0xFF>>h_8__8;//(8 - h%8);                         //取低位  
  106.                     lcd_screen_data[SSD1306_WIDTH*i + x + j] &= 0xFF<<h_8;//(h%8);     //删除低位  
  107.                     lcd_screen_data[SSD1306_WIDTH*i + x + j] |= tmp;  
  108.             } else {  
  109.                     lcd_screen_data[SSD1306_WIDTH*i + x + j] = tmp;  
  110.                 }  
  111.         }  
  112.         }  
  113.     }  
  114.     return 0;  
  115. }  
        先到这里,下一节介绍系统时钟的获取和刷新部分。