ssd13x6.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /**
  2. * Copyright (c) 2017-2018 Tara Keeling
  3. *
  4. * This software is released under the MIT License.
  5. * https://opensource.org/licenses/MIT
  6. */
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <stdint.h>
  10. #include <stdbool.h>
  11. #include <stdlib.h>
  12. #include <math.h>
  13. #include <esp_heap_caps.h>
  14. #include "ssd13x6.h"
  15. // used by both but different
  16. static uint8_t SSDCmd_Set_Display_Start_Line;
  17. static uint8_t SSDCmd_Set_Display_Offset;
  18. static uint8_t SSDCmd_Set_Column_Address;
  19. static uint8_t SSDCmd_Set_Display_CLK;
  20. static uint8_t SSDCmd_Set_Page_Address;
  21. // misc boundaries
  22. static uint8_t SSD13x6_Max_Col;
  23. static const uint8_t SSD13x6_Max_Row = 7;
  24. static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height );
  25. int SSD13x6_GetCaps( struct SSD13x6_Device* DeviceHandle ) {
  26. if (DeviceHandle->Model == SH1106) return 0;
  27. else return CAPS_COLUMN_RANGE | CAPS_PAGE_RANGE | CAPS_ADDRESS_VERTICAL;
  28. }
  29. bool SSD13x6_WriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd SSDCommand ) {
  30. NullCheck( DeviceHandle->WriteCommand, return false );
  31. return ( DeviceHandle->WriteCommand ) ( DeviceHandle, SSDCommand );
  32. }
  33. bool SSD13x6_WriteData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
  34. NullCheck( DeviceHandle->WriteData, return false );
  35. return ( DeviceHandle->WriteData ) ( DeviceHandle, Data, DataLength );
  36. }
  37. void SSD13x6_SetMuxRatio( struct SSD13x6_Device* DeviceHandle, uint8_t Ratio ) {
  38. SSD13x6_WriteCommand( DeviceHandle, 0xA8 );
  39. SSD13x6_WriteCommand( DeviceHandle, Ratio );
  40. }
  41. void SSD13x6_SetDisplayOffset( struct SSD13x6_Device* DeviceHandle, uint8_t Offset ) {
  42. SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Offset );
  43. SSD13x6_WriteCommand( DeviceHandle, Offset );
  44. }
  45. void SSD13x6_SetDisplayStartLine( struct SSD13x6_Device* DeviceHandle, int Line ) {
  46. SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Start_Line + ( uint32_t ) ( Line & 0x1F ) );
  47. }
  48. void SSD13x6_SetContrast( struct SSD13x6_Device* DeviceHandle, uint8_t Contrast ) {
  49. SSD13x6_WriteCommand( DeviceHandle, 0x81 );
  50. SSD13x6_WriteCommand( DeviceHandle, Contrast );
  51. }
  52. void SSD13x6_EnableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
  53. SSD13x6_WriteCommand( DeviceHandle, 0xA4 );
  54. }
  55. void SSD13x6_DisableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
  56. SSD13x6_WriteCommand( DeviceHandle, 0xA5 );
  57. }
  58. void SSD13x6_SetInverted( struct SSD13x6_Device* DeviceHandle, bool Inverted ) {
  59. SSD13x6_WriteCommand( DeviceHandle, Inverted ? 0xA7 : 0xA6 );
  60. }
  61. void SSD13x6_SetDisplayClocks( struct SSD13x6_Device* DeviceHandle, uint32_t DisplayClockDivider, uint32_t OSCFrequency ) {
  62. DisplayClockDivider&= 0x0F;
  63. OSCFrequency&= 0x0F;
  64. SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_CLK );
  65. SSD13x6_WriteCommand( DeviceHandle, ( ( OSCFrequency << 4 ) | DisplayClockDivider ) );
  66. }
  67. void SSD13x6_DisplayOn( struct SSD13x6_Device* DeviceHandle ) {
  68. SSD13x6_WriteCommand( DeviceHandle, 0xAF );
  69. }
  70. void SSD13x6_DisplayOff( struct SSD13x6_Device* DeviceHandle ) {
  71. SSD13x6_WriteCommand( DeviceHandle, 0xAE );
  72. }
  73. void SSD132x_ReMap( struct SSD13x6_Device* DeviceHandle ) {
  74. SSD13x6_WriteCommand( DeviceHandle, 0xA0 );
  75. SSD13x6_WriteCommand( DeviceHandle, DeviceHandle->ReMap );
  76. }
  77. void SSD13x6_SetDisplayAddressMode( struct SSD13x6_Device* DeviceHandle, SSD13x6_AddressMode AddressMode ) {
  78. switch (DeviceHandle->Model) {
  79. case SH1106:
  80. // does not exist on SH1106
  81. break;
  82. case SSD1306:
  83. SSD13x6_WriteCommand( DeviceHandle, 0x20 );
  84. SSD13x6_WriteCommand( DeviceHandle, AddressMode );
  85. break;
  86. case SSD1326:
  87. DeviceHandle->ReMap = (AddressMode == AddressMode_Horizontal) ? (DeviceHandle->ReMap & ~0x80) : (DeviceHandle->ReMap | 0x80);
  88. SSD132x_ReMap(DeviceHandle);
  89. break;
  90. }
  91. }
  92. void SSD13x6_Update( struct SSD13x6_Device* DeviceHandle ) {
  93. if (DeviceHandle->Model == SH1106) {
  94. // SH1106 requires a page-by-page update and ahs no end Page/Column
  95. for (int i = 0; i < DeviceHandle->Height / 8 ; i++) {
  96. SSD13x6_SetPageAddress( DeviceHandle, i, 0);
  97. SSD13x6_SetColumnAddress( DeviceHandle, 0, 0);
  98. SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer + i*DeviceHandle->Width, DeviceHandle->Width );
  99. }
  100. } else {
  101. // others have an automatic counter and end Page/Column
  102. SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1);
  103. SSD13x6_SetPageAddress( DeviceHandle, 0, DeviceHandle->Height / 8 - 1);
  104. SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer, DeviceHandle->FramebufferSize );
  105. }
  106. }
  107. void SSD13x6_WriteRawData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
  108. NullCheck( Data, return );
  109. DataLength = DataLength > DeviceHandle->FramebufferSize ? DeviceHandle->FramebufferSize : DataLength;
  110. if ( DataLength > 0 ) SSD13x6_WriteData( DeviceHandle, Data, DataLength );
  111. }
  112. void SSD13x6_SetHFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
  113. switch (DeviceHandle->Model) {
  114. case SH1106:
  115. case SSD1306:
  116. SSD13x6_WriteCommand( DeviceHandle, On ? 0xA1 : 0xA0 );
  117. break;
  118. case SSD1326:
  119. DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x01) : (DeviceHandle->ReMap & ~0x01);
  120. SSD132x_ReMap(DeviceHandle);
  121. break;
  122. }
  123. }
  124. void SSD13x6_SetVFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
  125. switch (DeviceHandle->Model) {
  126. case SH1106:
  127. case SSD1306:
  128. SSD13x6_WriteCommand( DeviceHandle, On ? 0xC8 : 0xC0 );
  129. break;
  130. case SSD1326:
  131. DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x05) : (DeviceHandle->ReMap & ~0x05);
  132. SSD132x_ReMap( DeviceHandle );
  133. break;
  134. }
  135. }
  136. void SSD13x6_SetColumnAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
  137. CheckBounds( Start > SSD13x6_Max_Col, return );
  138. CheckBounds( End > SSD13x6_Max_Col, return );
  139. // on SH1106, there is no "end column"
  140. if (DeviceHandle->Model == SH1106) {
  141. // well, unfortunately this driver is 132 colums but most displays are 128...
  142. if (DeviceHandle->Width != 132) Start += 2;
  143. SSD13x6_WriteCommand( DeviceHandle, 0x10 | (Start >> 4) );
  144. SSD13x6_WriteCommand( DeviceHandle, 0x00 | (Start & 0x0f) );
  145. } else {
  146. SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Column_Address );
  147. SSD13x6_WriteCommand( DeviceHandle, Start );
  148. SSD13x6_WriteCommand( DeviceHandle, End );
  149. }
  150. }
  151. void SSD13x6_SetPageAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
  152. NullCheck( DeviceHandle, return );
  153. CheckBounds( Start > SSD13x6_Max_Row, return );
  154. CheckBounds( End > SSD13x6_Max_Row, return );
  155. // on SH1106, there is no "end page"
  156. if (DeviceHandle->Model == SH1106) {
  157. SSD13x6_WriteCommand( DeviceHandle, 0xB0 | Start );
  158. } else {
  159. // in case of SSD1326, this is sub-optimal as it can address by line, not by page
  160. if (DeviceHandle->Model != SSD1306) {
  161. Start *= 8;
  162. End = (End + 1) * 8 - 1;
  163. }
  164. SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Page_Address );
  165. SSD13x6_WriteCommand( DeviceHandle, Start );
  166. SSD13x6_WriteCommand( DeviceHandle, End );
  167. }
  168. }
  169. bool SSD13x6_HWReset( struct SSD13x6_Device* DeviceHandle ) {
  170. NullCheck( DeviceHandle, return 0 );
  171. if ( DeviceHandle->Reset != NULL ) {
  172. return ( DeviceHandle->Reset ) ( DeviceHandle );
  173. }
  174. /* This should always return true if there is no reset callback as
  175. * no error would have occurred during the non existant reset.
  176. */
  177. return true;
  178. }
  179. static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height ) {
  180. DeviceHandle->Width = Width;
  181. DeviceHandle->Height = Height;
  182. SSD13x6_HWReset( DeviceHandle );
  183. SSD13x6_DisplayOff( DeviceHandle );
  184. if (DeviceHandle->Model == SSD1306 || DeviceHandle->Model == SH1106) {
  185. SSDCmd_Set_Display_Start_Line = 0x40;
  186. SSDCmd_Set_Display_Offset = 0xD3;
  187. SSDCmd_Set_Column_Address = 0x21,
  188. SSDCmd_Set_Display_CLK = 0xD5;
  189. SSDCmd_Set_Page_Address = 0x22;
  190. SSD13x6_Max_Col = 127;
  191. if (DeviceHandle->Model == SSD1306) {
  192. // charge pump regulator, do direct init
  193. SSD13x6_WriteCommand( DeviceHandle, 0x8D );
  194. SSD13x6_WriteCommand( DeviceHandle, 0x14 );
  195. // COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something difference
  196. SSD13x6_WriteCommand( DeviceHandle, 0xDA );
  197. SSD13x6_WriteCommand( DeviceHandle, ((Height == 64 ? 1 : 0) << 4) | (0 < 5) );
  198. } else {
  199. // charge pump regulator, do direct init
  200. SSD13x6_WriteCommand( DeviceHandle, 0xAD );
  201. SSD13x6_WriteCommand( DeviceHandle, 0x8B );
  202. // COM pins HW config (alternative:EN) - some display might need something difference
  203. SSD13x6_WriteCommand( DeviceHandle, 0xDA );
  204. SSD13x6_WriteCommand( DeviceHandle, 1 << 4);
  205. }
  206. } else if (DeviceHandle->Model == SSD1326) {
  207. SSDCmd_Set_Display_Start_Line = 0xA1;
  208. SSDCmd_Set_Display_Offset = 0xA2;
  209. SSDCmd_Set_Column_Address = 0x15;
  210. SSDCmd_Set_Display_CLK = 0xB3;
  211. SSDCmd_Set_Page_Address = 0x75; // not really a page but a row
  212. SSD13x6_Max_Col = 255;
  213. // no gray scale
  214. DeviceHandle->ReMap |= 0x10;
  215. SSD132x_ReMap( DeviceHandle );
  216. }
  217. SSD13x6_SetMuxRatio( DeviceHandle, Height - 1 );
  218. SSD13x6_SetDisplayOffset( DeviceHandle, 0x00 );
  219. SSD13x6_SetDisplayStartLine( DeviceHandle, 0 );
  220. SSD13x6_SetContrast( DeviceHandle, 0x7F );
  221. SSD13x6_DisableDisplayRAM( DeviceHandle );
  222. SSD13x6_SetVFlip( DeviceHandle, false );
  223. SSD13x6_SetHFlip( DeviceHandle, false );
  224. SSD13x6_SetInverted( DeviceHandle, false );
  225. SSD13x6_SetDisplayClocks( DeviceHandle, 0, 8 );
  226. SSD13x6_SetDisplayAddressMode( DeviceHandle, AddressMode_Horizontal );
  227. SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1 );
  228. SSD13x6_SetPageAddress( DeviceHandle, 0, ( DeviceHandle->Height / 8 ) - 1 );
  229. SSD13x6_EnableDisplayRAM( DeviceHandle );
  230. SSD13x6_DisplayOn( DeviceHandle );
  231. SSD13x6_Update( DeviceHandle );
  232. return true;
  233. }
  234. bool SSD13x6_Init_I2C( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int I2CAddress, int ResetPin, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
  235. NullCheck( DeviceHandle, return false );
  236. NullCheck( WriteCommand, return false );
  237. NullCheck( WriteData, return false );
  238. DeviceHandle->WriteCommand = WriteCommand;
  239. DeviceHandle->WriteData = WriteData;
  240. DeviceHandle->Reset = Reset;
  241. DeviceHandle->Address = I2CAddress;
  242. DeviceHandle->RSTPin = ResetPin;
  243. DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
  244. DeviceHandle->Framebuffer = calloc( 1, DeviceHandle->FramebufferSize );
  245. NullCheck( DeviceHandle->Framebuffer, return false );
  246. return SSD13x6_Init( DeviceHandle, Width, Height );
  247. }
  248. bool SSD13x6_Init_SPI( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int ResetPin, int CSPin, spi_device_handle_t SPIHandle, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
  249. NullCheck( DeviceHandle, return false );
  250. NullCheck( WriteCommand, return false );
  251. NullCheck( WriteData, return false );
  252. DeviceHandle->WriteCommand = WriteCommand;
  253. DeviceHandle->WriteData = WriteData;
  254. DeviceHandle->Reset = Reset;
  255. DeviceHandle->SPIHandle = SPIHandle;
  256. DeviceHandle->RSTPin = ResetPin;
  257. DeviceHandle->CSPin = CSPin;
  258. DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
  259. DeviceHandle->Framebuffer = heap_caps_calloc( 1, DeviceHandle->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
  260. NullCheck( DeviceHandle->Framebuffer, return false );
  261. return SSD13x6_Init( DeviceHandle, Width, Height );
  262. }