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