1 /*
2  * Copyright (c) 2022-2024, Xiaohua Semiconductor Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-12-30     CDT          first version
9  */
10 
11 /*
12  * 程序清单:这是 Soft I2C 设备使用例程。
13  * 例程导出了 sw_i2c_sample 到控制终端。
14  * 命令调用格式:sw_i2c_sample cmd_id [options]
15  * 命令解释:命令第二个参数是要使用的Soft I2C设备的命令,为空则打印命令使用说明
16  * 程序功能:查找Soft I2C设备,读写I2C设备寄存器。
17  * 注意:测试要用逻辑分析仪或示波器抓取信号
18 */
19 
20 #include <rtthread.h>
21 #include <rtdevice.h>
22 #include <board.h>
23 #include "rtconfig.h"
24 #include "rtdef.h"
25 
26 #if defined(RT_USING_I2C)
27 
28 #if defined(BSP_USING_I2C1_SW)
29 
30 /* using i2c1 control oled12864 */
31 #define SW_I2C_NAME         "i2c1_sw"
32 #define SSD1306_ADDR        (0x78U >> 1)
33 #define SSD1306_MD_CMD      (0x00U)
34 #define SSD1306_MD_DATA     (0x40U)
35 
36 /* symbol parameters: width pixles, lenght pixels */
37 #define SYM_W_PIX           (8U)
38 #define SYM_H_PIX           (8U)
39 /* ssd ohysical parameters */
40 #define SSD_PAGE_SIZE       (8U)
41 #define SSD_COL_SIZE        (128U)
42 /* each page 8 pix */
43 #define PAGE_PIX_SIZE       (8U)
44 
45 /* each byte set horizontal 1 pix */
46 #define SYM_W_BYTE          (SYM_W_PIX / 1)
47 /* each byte set vertical 8 pix */
48 #define SYM_H_BYTE          (SYM_H_PIX / 8)
49 /* each character occupis */
50 #define SYM_BYTE_SIZE       (SYM_W_BYTE * SYM_H_BYTE)
51 
52 /* soft i2c command defines */
53 enum SW_I2C_CMD
54 {
55     SW_I2C_INIT = 0x01,
56     SSD1306_CMD_INIT,
57     SSD1306_CMD_DISPLAY,
58     SSD1306_CMD_DEINIT,
59 };
60 
61 /* local variables */
62 struct rt_i2c_msg msg;
63 rt_uint8_t ssd_init_array[30];
64 rt_uint8_t logo_array[5][SYM_BYTE_SIZE];
65 
66 /* local functions */
67 static void ssd1306_init(struct rt_i2c_bus_device *i2c_dev);
68 static void ssd1306_roll_display(struct rt_i2c_bus_device *i2c_dev);
69 static void ssd1306_deinit(struct rt_i2c_bus_device *i2c_dev);
70 
71 /* write_reg ssd1306 basic opertion */
ssd1306_write_single_reg(struct rt_i2c_bus_device * i2c_dev,rt_bool_t is_write_cmd,rt_uint8_t data)72 static void ssd1306_write_single_reg(struct rt_i2c_bus_device *i2c_dev,
73                                      rt_bool_t is_write_cmd, rt_uint8_t data)
74 {
75     rt_uint8_t buff[2];
76     struct rt_i2c_msg msgs;
77 
78     msgs.addr = SSD1306_ADDR;
79     msgs.flags = RT_I2C_WR;
80 
81     if (RT_TRUE == is_write_cmd)
82     {
83         buff[0] = SSD1306_MD_CMD;
84     }
85     else
86     {
87         buff[0] = SSD1306_MD_DATA;
88     }
89 
90     buff[1] = data;
91     msgs.buf = buff;
92     msgs.len = 2;
93 
94     if (1 != rt_i2c_transfer(i2c_dev, &msgs, 1))
95     {
96         rt_kprintf("failed to send cmd\n");
97     }
98 }
99 
100 /* write_reg ssd1306 basic opertion */
ssd1306_write_mult_reg(struct rt_i2c_bus_device * i2c_dev,rt_bool_t is_write_cmd,rt_uint8_t len,rt_uint8_t * data)101 static void ssd1306_write_mult_reg(struct rt_i2c_bus_device *i2c_dev,
102                                    rt_bool_t is_write_cmd, rt_uint8_t len, rt_uint8_t *data
103                                    /*rt_uint8_t data*/)
104 {
105     rt_uint8_t *buff = NULL;
106     struct rt_i2c_msg msgs;
107 
108     msgs.addr = SSD1306_ADDR;
109     msgs.flags = RT_I2C_WR;
110     buff = (rt_uint8_t *)rt_malloc((len + 1) * sizeof(rt_uint8_t));
111 
112     if (RT_TRUE == is_write_cmd)
113     {
114         buff[0] = SSD1306_MD_CMD;
115     }
116     else
117     {
118         buff[0] = SSD1306_MD_DATA;
119     }
120     rt_memcpy(buff + 1, data, len);
121     msgs.buf = buff;
122     msgs.len = len + 1;
123 
124     if (1 != rt_i2c_transfer(i2c_dev, &msgs, 1))
125     {
126         rt_kprintf("failed to send cmd\n");
127     }
128     rt_free(buff);
129     buff = NULL;
130 }
131 
sw_i2c_sample(int argc,char * argv[])132 static int sw_i2c_sample(int argc, char *argv[])
133 {
134     static struct rt_i2c_bus_device *rt_i2c_dev;
135     /* print soft i2c usage */
136     if (argc <= 1)
137     {
138         rt_kprintf("soft i2c usage as following:\n");
139         rt_kprintf("sw_i2c_sample %d: soft i2c init\n", SW_I2C_INIT);
140         rt_kprintf("sw_i2c_sample %d: oled ssd1306 init\n", SSD1306_CMD_INIT);
141         rt_kprintf("sw_i2c_sample %d: write ssd1306 \n", SSD1306_CMD_DISPLAY);
142         rt_kprintf("sw_i2c_sample %d: turn off ssd1306\n", SSD1306_CMD_DEINIT);
143         return -RT_ERROR;
144     }
145 
146     switch (*argv[1] - '0')
147     {
148     case SW_I2C_INIT:
149         rt_i2c_dev = (struct rt_i2c_bus_device *)rt_device_find(SW_I2C_NAME);
150         if (NULL == rt_i2c_dev)
151         {
152             rt_kprintf("failed to find i2c device %s\n", SW_I2C_NAME);
153             return -RT_ERROR;
154         }
155         break;
156     /* communicate with eeprom to soft i2c read function */
157     case SSD1306_CMD_INIT:
158         ssd1306_init(rt_i2c_dev);
159         break;
160     /* communicate with ssd1306 to soft i2c read function */
161     case SSD1306_CMD_DISPLAY:
162         ssd1306_roll_display(rt_i2c_dev);
163         break;
164     case SSD1306_CMD_DEINIT:
165         ssd1306_deinit(rt_i2c_dev);
166         break;
167     default:
168         rt_kprintf("unkwon command\n");
169         break;
170     }
171 
172     return RT_EOK;
173 }
174 
175 /* ssd1306 de-init and turn off */
ssd1306_deinit(struct rt_i2c_bus_device * i2c_dev)176 static void ssd1306_deinit(struct rt_i2c_bus_device *i2c_dev)
177 {
178     rt_uint8_t ssd_deinit_array[] =
179     {
180         0X8D, /* set charge pump */
181         0X10, /* turn off charge pump */
182         0XAE, /* OLED sleep */
183     };
184 
185     ssd1306_write_mult_reg(i2c_dev, RT_TRUE,
186                            sizeof(ssd_deinit_array) / sizeof(ssd_deinit_array[0]),
187                            ssd_deinit_array);
188 }
189 
190 /* ssd oled initialize */
ssd1306_init(struct rt_i2c_bus_device * i2c_dev)191 static void ssd1306_init(struct rt_i2c_bus_device *i2c_dev)
192 {
193     ssd1306_write_mult_reg(i2c_dev, RT_TRUE,
194                            sizeof(ssd_init_array) / sizeof(ssd_init_array[0]),
195                            ssd_init_array);
196 }
197 
198 /*
199     Function:   write a heigh * width = 16 *16 --->pixel: 16 * 8character
200     Input:      void
201     Output:     void
202     Data:       20210828
203 */
204 
mOledWriteCharHnWm(struct rt_i2c_bus_device * i2c_dev,uint8_t page,uint8_t col,uint8_t * ArrChar)205 void mOledWriteCharHnWm(struct rt_i2c_bus_device *i2c_dev,
206                         uint8_t page, uint8_t col, uint8_t *ArrChar)
207 {
208     if (ArrChar == NULL)
209         return;
210     rt_kprintf("x=%3d, y=%d\n", col, page);
211     for (uint8_t page_idx = 0; page_idx < SYM_H_BYTE; page_idx++)
212     {
213         /* set start page: page0-page1 */
214         ssd1306_write_single_reg(i2c_dev, RT_TRUE, 0xb0 + page + page_idx);
215         /* lower 4-bit address of column start */
216         ssd1306_write_single_reg(i2c_dev, RT_TRUE, 0x00 + ((col & 0x0F) >> 0));
217         /* higher 4-bit address of column start */
218         ssd1306_write_single_reg(i2c_dev, RT_TRUE, 0x10 + ((col & 0xF0) >> 4));
219         /* send a character(total BYTE_CHAR byte) from array */
220         ssd1306_write_mult_reg(i2c_dev, RT_FALSE, SYM_W_BYTE,
221                                ArrChar + SYM_W_BYTE * page_idx);
222     }
223 }
224 
225 /* fill oled with character "XHSC" */
ssd1306_roll_display(struct rt_i2c_bus_device * i2c_dev)226 static void ssd1306_roll_display(struct rt_i2c_bus_device *i2c_dev)
227 {
228     rt_uint8_t base_col, base_page;
229     rt_uint8_t offset_page, offset_col;
230     rt_uint16_t idx;
231     /* using a write times related variable control position */
232     static rt_uint16_t u16WriteTimes = 0;
233 
234     if (u16WriteTimes >= (SSD_COL_SIZE / SYM_W_PIX) * (SSD_PAGE_SIZE / (SYM_H_PIX / PAGE_PIX_SIZE)))
235     {
236         u16WriteTimes = 0;
237     }
238     /* each page write, eg. base_page = 7 / (128 / 8) = 0 */
239     base_page = u16WriteTimes / (SSD_COL_SIZE / SYM_W_PIX);
240     /* eg. base_col = (7 % (128 / 8)) * 8 = 56 */
241     base_col = (u16WriteTimes % (SSD_COL_SIZE / SYM_W_PIX)) * SYM_W_PIX;
242     offset_page = 0;
243     offset_col = 0;
244     /* each write cycle finish the data writing in array */
245     for (idx = 0; idx < sizeof(logo_array) / sizeof(logo_array[0]); idx++)
246     {
247         offset_col = idx * SYM_W_PIX;
248         if (base_col + offset_col >= SSD_COL_SIZE)
249         {
250             /*
251                 base_col + offset_col = [128, 256), page_offset = 1,
252                 base_col + offset_col = [256, 384), page_offset = 2,
253                 ...
254             */
255             offset_page = (base_col + offset_col) / SSD_COL_SIZE;
256         }
257         mOledWriteCharHnWm(i2c_dev, ((base_page + offset_page) * SYM_H_BYTE) % SSD_PAGE_SIZE, (base_col + offset_col) % SSD_COL_SIZE, *(logo_array + idx));
258     }
259     u16WriteTimes++;
260 }
261 
262 rt_uint8_t ssd_init_array[] =
263 {
264     0xAE, /* display off */
265     0x20, /* Set Memory Addressing Mode */
266     0x10, /* Set addressing orient */
267     0xB0, /* Set Page Start Address for Page Addressing Mode,0-7 */
268     0xC8, /* Set COM Output Scan Direction */
269     0x00, /* set low column address */
270     0x10, /* set high column address */
271     0x40, /* set start line address */
272     0x81, /* set contrast control register */
273     0xFF, /* breightness 0x00~0xff */
274     0xA1, /* set segment re-map 0 to 127 */
275     0xA6, /* set normal display */
276     0xA8, /* set multiplex ratio(1 to 64) */
277     0x3F, /*      */
278     0xC8, /*     */
279     0xA4, /* 0xa4,Output follows RAM content;0xa5,Output ignores RAM content */
280     0xD3, /* set display offset */
281     0x00, /* not offset */
282     0xD5, /* set display clock divide ratio/oscillator frequency */
283     0xF0, /* set divide ratio */
284     0xD9, /* set pre-charge period */
285     0x22, /*  */
286     0xDA, /* set com pins hardware configuration */
287     0x12, /*  */
288     0xDB, /* set vcomh */
289     0x20, /* 0x20,0.77xVcc */
290     0x8D, /* set DC-DC enable */
291     0x14, /*  */
292     0xAF, /* --turn on oled panel */
293 };
294 
295 
296 rt_uint8_t logo_array[][SYM_BYTE_SIZE] =
297 {
298     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299     0x44, 0x44, 0x6C, 0x74, 0x54, 0x6C, 0x44, 0x44, /*"X"*/
300     0x44, 0x7C, 0x54, 0x10, 0x10, 0x54, 0x7C, 0x44, /*"H"*/
301     0x00, 0x68, 0x54, 0x54, 0x54, 0x54, 0x24, 0x00, /*"S"*/
302     0x38, 0x6C, 0x44, 0x44, 0x44, 0x44, 0x24, 0x00, /*"C"*/
303 };
304 
305 MSH_CMD_EXPORT(sw_i2c_sample, soft i2c sample);
306 
307 #endif /* BSP_USING_I2C1_SW */
308 
309 #endif/* RT_USING_I2C */
310 /*
311 EOF
312 */
313