ssd1306.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. #include "../ssd1306/ssd1306.h"
  2. #include <math.h>
  3. #include <stdlib.h>
  4. #include <string.h> // For memcpy
  5. #if defined(SSD1306_USE_I2C)
  6. void ssd1306_Reset(void) {
  7. /* for I2C - do nothing */
  8. }
  9. // Send a byte to the command register
  10. void ssd1306_WriteCommand(uint8_t byte) {
  11. HAL_I2C_Mem_Write(&SSD1306_I2C_PORT, SSD1306_I2C_ADDR, 0x00, 1, &byte, 1, HAL_MAX_DELAY);
  12. }
  13. // Send data
  14. void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {
  15. HAL_I2C_Mem_Write(&SSD1306_I2C_PORT, SSD1306_I2C_ADDR, 0x40, 1, buffer, buff_size, HAL_MAX_DELAY);
  16. }
  17. #elif defined(SSD1306_USE_SPI)
  18. void ssd1306_Reset(void) {
  19. // CS = High (not selected)
  20. HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET);
  21. // Reset the OLED
  22. HAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_RESET);
  23. HAL_Delay(10);
  24. HAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_SET);
  25. HAL_Delay(10);
  26. }
  27. // Send a byte to the command register
  28. void ssd1306_WriteCommand(uint8_t byte) {
  29. HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLED
  30. HAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_RESET); // command
  31. HAL_SPI_Transmit(&SSD1306_SPI_PORT, (uint8_t *) &byte, 1, HAL_MAX_DELAY);
  32. HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
  33. }
  34. // Send data
  35. void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {
  36. HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLED
  37. HAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_SET); // data
  38. HAL_SPI_Transmit(&SSD1306_SPI_PORT, buffer, buff_size, HAL_MAX_DELAY);
  39. HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
  40. }
  41. #else
  42. #error "You should define SSD1306_USE_SPI or SSD1306_USE_I2C macro"
  43. #endif
  44. // Screenbuffer
  45. static uint8_t SSD1306_Buffer[SSD1306_BUFFER_SIZE];
  46. // Screen object
  47. static SSD1306_t SSD1306;
  48. /* Fills the Screenbuffer with values from a given buffer of a fixed length */
  49. SSD1306_Error_t ssd1306_FillBuffer(uint8_t* buf, uint32_t len) {
  50. SSD1306_Error_t ret = SSD1306_ERR;
  51. if (len <= SSD1306_BUFFER_SIZE) {
  52. memcpy(SSD1306_Buffer,buf,len);
  53. ret = SSD1306_OK;
  54. }
  55. return ret;
  56. }
  57. // Initialize the oled screen
  58. void ssd1306_Init(void) {
  59. // Reset OLED
  60. ssd1306_Reset();
  61. // Wait for the screen to boot
  62. HAL_Delay(100);
  63. // Init OLED
  64. ssd1306_SetDisplayOn(0); //display off
  65. ssd1306_WriteCommand(0x20); //Set Memory Addressing Mode
  66. ssd1306_WriteCommand(0x00); // 00b,Horizontal Addressing Mode; 01b,Vertical Addressing Mode;
  67. // 10b,Page Addressing Mode (RESET); 11b,Invalid
  68. ssd1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode,0-7
  69. #ifdef SSD1306_MIRROR_VERT
  70. ssd1306_WriteCommand(0xC0); // Mirror vertically
  71. #else
  72. ssd1306_WriteCommand(0xC8); //Set COM Output Scan Direction
  73. #endif
  74. ssd1306_WriteCommand(0x00); //---set low column address
  75. ssd1306_WriteCommand(0x10); //---set high column address
  76. ssd1306_WriteCommand(0x40); //--set start line address - CHECK
  77. ssd1306_SetContrast(0xFF);
  78. #ifdef SSD1306_MIRROR_HORIZ
  79. ssd1306_WriteCommand(0xA0); // Mirror horizontally
  80. #else
  81. ssd1306_WriteCommand(0xA1); //--set segment re-map 0 to 127 - CHECK
  82. #endif
  83. #ifdef SSD1306_INVERSE_COLOR
  84. ssd1306_WriteCommand(0xA7); //--set inverse color
  85. #else
  86. ssd1306_WriteCommand(0xA6); //--set normal color
  87. #endif
  88. // Set multiplex ratio.
  89. #if (SSD1306_HEIGHT == 128)
  90. // Found in the Luma Python lib for SH1106.
  91. ssd1306_WriteCommand(0xFF);
  92. #else
  93. ssd1306_WriteCommand(0xA8); //--set multiplex ratio(1 to 64) - CHECK
  94. #endif
  95. #if (SSD1306_HEIGHT == 32)
  96. ssd1306_WriteCommand(0x1F); //
  97. #elif (SSD1306_HEIGHT == 64)
  98. ssd1306_WriteCommand(0x3F); //
  99. #elif (SSD1306_HEIGHT == 128)
  100. ssd1306_WriteCommand(0x3F); // Seems to work for 128px high displays too.
  101. #else
  102. #error "Only 32, 64, or 128 lines of height are supported!"
  103. #endif
  104. ssd1306_WriteCommand(0xA4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  105. ssd1306_WriteCommand(0xD3); //-set display offset - CHECK
  106. ssd1306_WriteCommand(0x00); //-not offset
  107. ssd1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequency
  108. ssd1306_WriteCommand(0xF0); //--set divide ratio
  109. ssd1306_WriteCommand(0xD9); //--set pre-charge period
  110. ssd1306_WriteCommand(0x22); //
  111. ssd1306_WriteCommand(0xDA); //--set com pins hardware configuration - CHECK
  112. #if (SSD1306_HEIGHT == 32)
  113. ssd1306_WriteCommand(0x02);
  114. #elif (SSD1306_HEIGHT == 64)
  115. ssd1306_WriteCommand(0x12);
  116. #elif (SSD1306_HEIGHT == 128)
  117. ssd1306_WriteCommand(0x12);
  118. #else
  119. #error "Only 32, 64, or 128 lines of height are supported!"
  120. #endif
  121. ssd1306_WriteCommand(0xDB); //--set vcomh
  122. ssd1306_WriteCommand(0x20); //0x20,0.77xVcc
  123. ssd1306_WriteCommand(0x8D); //--set DC-DC enable
  124. ssd1306_WriteCommand(0x14); //
  125. ssd1306_SetDisplayOn(1); //--turn on SSD1306 panel
  126. // Clear screen
  127. ssd1306_Fill(Black);
  128. // Flush buffer to screen
  129. ssd1306_UpdateScreen();
  130. // Set default values for screen object
  131. SSD1306.CurrentX = 0;
  132. SSD1306.CurrentY = 0;
  133. SSD1306.Initialized = 1;
  134. }
  135. // Fill the whole screen with the given color
  136. void ssd1306_Fill(SSD1306_COLOR color) {
  137. /* Set memory */
  138. uint32_t i;
  139. for(i = 0; i < sizeof(SSD1306_Buffer); i++) {
  140. SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF;
  141. }
  142. }
  143. // Write the screenbuffer with changed to the screen
  144. void ssd1306_UpdateScreen(void) {
  145. // Write data to each page of RAM. Number of pages
  146. // depends on the screen height:
  147. //
  148. // * 32px == 4 pages
  149. // * 64px == 8 pages
  150. // * 128px == 16 pages
  151. for(uint8_t i = 0; i < SSD1306_HEIGHT/8; i++) {
  152. ssd1306_WriteCommand(0xB0 + i); // Set the current RAM page address.
  153. ssd1306_WriteCommand(0x00);
  154. ssd1306_WriteCommand(0x10);
  155. ssd1306_WriteData(&SSD1306_Buffer[SSD1306_WIDTH*i],SSD1306_WIDTH);
  156. }
  157. }
  158. // Draw one pixel in the screenbuffer
  159. // X => X Coordinate
  160. // Y => Y Coordinate
  161. // color => Pixel color
  162. void ssd1306_DrawPixel(int x, int y, SSD1306_COLOR color) {
  163. if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT || x < 0 || y < 0) {
  164. // Don't write outside the buffer
  165. return;
  166. }
  167. // Check if pixel should be inverted
  168. if(SSD1306.Inverted) {
  169. color = (SSD1306_COLOR)!color;
  170. }
  171. // Draw in the right color
  172. if(color == White) {
  173. SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);
  174. } else {
  175. SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));
  176. }
  177. }
  178. // Draw 1 char to the screen buffer
  179. // ch => char om weg te schrijven
  180. // Font => Font waarmee we gaan schrijven
  181. // color => Black or White
  182. char ssd1306_WriteChar(char ch, FontDef Font, SSD1306_COLOR color) {
  183. uint32_t i, b, j;
  184. // Check if character is valid
  185. if (ch < 32 || ch > 126)
  186. return 0;
  187. // Check remaining space on current line
  188. /*
  189. if (SSD1306_WIDTH < (SSD1306.CurrentX + Font.FontWidth) ||
  190. SSD1306_HEIGHT < (SSD1306.CurrentY + Font.FontHeight))
  191. {
  192. // Not enough space on current line
  193. return 0;
  194. }
  195. */
  196. // Use the font to write
  197. for(i = 0; i < Font.FontHeight; i++) {
  198. b = Font.data[(ch - 32) * Font.FontHeight + i];
  199. for(j = 0; j < Font.FontWidth; j++) {
  200. if((b << j) & 0x8000) {
  201. ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);
  202. } else {
  203. ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);
  204. }
  205. }
  206. }
  207. // The current space is now taken
  208. SSD1306.CurrentX += Font.FontWidth;
  209. // Return written char for validation
  210. return ch;
  211. }
  212. // Write full string to screenbuffer
  213. char ssd1306_WriteString(char* str, FontDef Font, SSD1306_COLOR color) {
  214. // Write until null-byte
  215. while (*str) {
  216. if (ssd1306_WriteChar(*str, Font, color) != *str) {
  217. // Char could not be written
  218. return *str;
  219. }
  220. // Next char
  221. str++;
  222. }
  223. // Everything ok
  224. return *str;
  225. }
  226. // Position the cursor
  227. void ssd1306_SetCursor(int x, int y) {
  228. SSD1306.CurrentX = x;
  229. SSD1306.CurrentY = y;
  230. }
  231. // Draw line by Bresenhem's algorithm
  232. void ssd1306_Line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {
  233. int32_t deltaX = abs(x2 - x1);
  234. int32_t deltaY = abs(y2 - y1);
  235. int32_t signX = ((x1 < x2) ? 1 : -1);
  236. int32_t signY = ((y1 < y2) ? 1 : -1);
  237. int32_t error = deltaX - deltaY;
  238. int32_t error2;
  239. ssd1306_DrawPixel(x2, y2, color);
  240. while((x1 != x2) || (y1 != y2))
  241. {
  242. ssd1306_DrawPixel(x1, y1, color);
  243. error2 = error * 2;
  244. if(error2 > -deltaY)
  245. {
  246. error -= deltaY;
  247. x1 += signX;
  248. }
  249. else
  250. {
  251. /*nothing to do*/
  252. }
  253. if(error2 < deltaX)
  254. {
  255. error += deltaX;
  256. y1 += signY;
  257. }
  258. else
  259. {
  260. /*nothing to do*/
  261. }
  262. }
  263. return;
  264. }
  265. //Draw polyline
  266. void ssd1306_Polyline(const SSD1306_VERTEX *par_vertex, uint16_t par_size, SSD1306_COLOR color) {
  267. uint16_t i;
  268. if(par_vertex != 0){
  269. for(i = 1; i < par_size; i++){
  270. ssd1306_Line(par_vertex[i - 1].x, par_vertex[i - 1].y, par_vertex[i].x, par_vertex[i].y, color);
  271. }
  272. }
  273. else
  274. {
  275. /*nothing to do*/
  276. }
  277. return;
  278. }
  279. /*Convert Degrees to Radians*/
  280. static float ssd1306_DegToRad(float par_deg) {
  281. return par_deg * 3.14 / 180.0;
  282. }
  283. /*Normalize degree to [0;360]*/
  284. static uint16_t ssd1306_NormalizeTo0_360(uint16_t par_deg) {
  285. uint16_t loc_angle;
  286. if(par_deg <= 360)
  287. {
  288. loc_angle = par_deg;
  289. }
  290. else
  291. {
  292. loc_angle = par_deg % 360;
  293. loc_angle = ((par_deg != 0)?par_deg:360);
  294. }
  295. return loc_angle;
  296. }
  297. /*DrawArc. Draw angle is beginning from 4 quart of trigonometric circle (3pi/2)
  298. * start_angle in degree
  299. * sweep in degree
  300. */
  301. void ssd1306_DrawArc(uint8_t x, uint8_t y, uint8_t radius, uint16_t start_angle, uint16_t sweep, SSD1306_COLOR color) {
  302. #define CIRCLE_APPROXIMATION_SEGMENTS 36
  303. float approx_degree;
  304. uint32_t approx_segments;
  305. uint8_t xp1,xp2;
  306. uint8_t yp1,yp2;
  307. uint32_t count = 0;
  308. uint32_t loc_sweep = 0;
  309. float rad;
  310. loc_sweep = ssd1306_NormalizeTo0_360(sweep);
  311. count = (ssd1306_NormalizeTo0_360(start_angle) * CIRCLE_APPROXIMATION_SEGMENTS) / 360;
  312. approx_segments = (loc_sweep * CIRCLE_APPROXIMATION_SEGMENTS) / 360;
  313. approx_degree = loc_sweep / (float)approx_segments;
  314. while(count < approx_segments)
  315. {
  316. rad = ssd1306_DegToRad(count*approx_degree);
  317. xp1 = x + (int8_t)(sin(rad)*radius);
  318. yp1 = y + (int8_t)(cos(rad)*radius);
  319. count++;
  320. if(count != approx_segments)
  321. {
  322. rad = ssd1306_DegToRad(count*approx_degree);
  323. }
  324. else
  325. {
  326. rad = ssd1306_DegToRad(loc_sweep);
  327. }
  328. xp2 = x + (int8_t)(sin(rad)*radius);
  329. yp2 = y + (int8_t)(cos(rad)*radius);
  330. ssd1306_Line(xp1,yp1,xp2,yp2,color);
  331. }
  332. return;
  333. }
  334. //Draw circle by Bresenhem's algorithm
  335. void ssd1306_DrawCircle(uint8_t par_x,uint8_t par_y,uint8_t par_r,SSD1306_COLOR par_color) {
  336. int32_t x = -par_r;
  337. int32_t y = 0;
  338. int32_t err = 2 - 2 * par_r;
  339. int32_t e2;
  340. if (par_x >= SSD1306_WIDTH || par_y >= SSD1306_HEIGHT) {
  341. return;
  342. }
  343. do {
  344. ssd1306_DrawPixel(par_x - x, par_y + y, par_color);
  345. ssd1306_DrawPixel(par_x + x, par_y + y, par_color);
  346. ssd1306_DrawPixel(par_x + x, par_y - y, par_color);
  347. ssd1306_DrawPixel(par_x - x, par_y - y, par_color);
  348. e2 = err;
  349. if (e2 <= y) {
  350. y++;
  351. err = err + (y * 2 + 1);
  352. if(-x == y && e2 <= x) {
  353. e2 = 0;
  354. }
  355. else
  356. {
  357. /*nothing to do*/
  358. }
  359. }
  360. else
  361. {
  362. /*nothing to do*/
  363. }
  364. if(e2 > x) {
  365. x++;
  366. err = err + (x * 2 + 1);
  367. }
  368. else
  369. {
  370. /*nothing to do*/
  371. }
  372. } while(x <= 0);
  373. return;
  374. }
  375. //Draw rectangle
  376. void ssd1306_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {
  377. ssd1306_Line(x1,y1,x2,y1,color);
  378. ssd1306_Line(x2,y1,x2,y2,color);
  379. ssd1306_Line(x2,y2,x1,y2,color);
  380. ssd1306_Line(x1,y2,x1,y1,color);
  381. return;
  382. }
  383. void ssd1306_SetContrast(const uint8_t value) {
  384. const uint8_t kSetContrastControlRegister = 0x81;
  385. ssd1306_WriteCommand(kSetContrastControlRegister);
  386. ssd1306_WriteCommand(value);
  387. }
  388. void ssd1306_SetDisplayOn(const uint8_t on) {
  389. uint8_t value;
  390. if (on) {
  391. value = 0xAF; // Display on
  392. SSD1306.DisplayOn = 1;
  393. } else {
  394. value = 0xAE; // Display off
  395. SSD1306.DisplayOn = 0;
  396. }
  397. ssd1306_WriteCommand(value);
  398. }
  399. uint8_t ssd1306_GetDisplayOn() {
  400. return SSD1306.DisplayOn;
  401. }