Привет всем, меня зовут Грей, я скромный специалист по embedded software, который любит исследовать новые технологии. Я всегда следил за HarmonyOS, но пропустил момент подачи заявки на получение разработочной платы. Сейчас у меня нет доступной разработочной платы, поэтому я использую чужие ресурсы после работы. Надеюсь, что официальные лица смогут отправить мне комплект разработочной платы Hi3861. Сегодня я хочу поделиться своим опытом по модификации OLED для использования с HarmonyOS. На самом деле это не совсем "перенос" — уже есть опытные разработчики, которые сделали это раньше, а я просто адаптировал некоторые функции, чтобы они были удобнее для использования программистами. Основное содержание сегодняшнего материала заключается в том, чтобы сделать вывод строки на OLED экран через printf
, поддерживающий одновременную отображение китайских и английских символов.
Вот пример:
ssd1306_Print(0, 0, "哈喽鸿蒙", White);
 ==》 
Или такой вариант:
ssd1306_Print(0, 0, "哈喽Harmony", White);
 ==》Даже можно использовать так:
sprintf(buff,"температура %d C",20);
ssd1306_Print(0,32,buff, White);
 ==》 
Не лучше ли использовать такие методы?
OLED_ShowCHinese(0,0,0);
OLED_ShowCHinese(16,0,1);
OLED_ShowCHinese(32,0,2);
OLED_ShowCHinese(48,0,3);
OLED_ShowCHinese(64,0,4);
OLED_ShowCHinese(80,0,5);
OLED_ShowCHinese(96,0,6); // Отображает "空气质量检测仪"
OLED_ShowString(0,2,"T:");
OLED_ShowNum(16,2,temperature,2,16); // Отображает значение температуры
OLED_ShowCHinese(32,2,8); // Отображает символ температуры
OLED_ShowString(56,2,"R:");
OLED_ShowChar(88,2,'%'); // Отображает процент
OLED_ShowNum(72,2,humidity,2,16);
Заявление: В рамках данной инструкции считается, что вы уже установили необходимую среду. Подробнее о процессе установки можно узнать здесь:
https://harmonyos.51cto.com/posts/1234
Инструкция по миграции готова, она основана на руководстве от преподавателя Xu Siwei, с некоторыми изменениями. Для доступа к инструкции перейдите по следующей ссылке:
Руководство предполагает скачивание кода в корневую директорию и его запуск. Однако, при работе над проектами мы обычно размещаем модули внешних устройств в папке app
, поэтому мой подход к миграции такой:1. Скачайте и загрузите код на сервер Linux. У меня используется виртуальная машина, поэтому я использую общие папки для загрузки кода на рабочий стол виртуальной машины. Затем выполните команду:```bash
unzip harmonyos-ssd1306-master.zip -d /home/harmony/harmony/code/code-1.0/applications/sample/wifi-iot/app/
Для распаковки файла в папку `app`:

После этого измените файл `BUILD.gn` в папке `app`, добавив `"harmonyos-ssd1306:app"`. Обратите внимание на точку с запятой.
Затем скомпилируйте и загрузите код.
### Как отображаются китайские символы
Как известно, OLED-дисплеи используют ASCII-код для отображения символов. Но как же отображаются китайские символы? Давайте рассмотрим некоторые аспекты кодировки китайских символов:
Китайские символы:
- Размер байт: 2; Кодировка: GB2312
- Размер байт: 2; Кодировка: GBK
- Размер байт: 2; Кодировка: GB18030
- Размер байт: 1; Кодировка: ISO-8859-1
- Размер байт: 3; Кодировка: UTF-8
- Размер байт: 4; Кодировка: UTF-16
- Размер байт: 2; Кодировка: UTF-16BE
- Размер байт: 2; Кодировка: UTF-16LE1. Американцы первыми закодировали английские символы, создав самый ранний ASCII-код, который использовал нижние 7 бит одного байта для представления 128 английских символов, а верхний бит всегда равнялся нулю;
2. Позже европейцы заметили, что этих 128 символов недостаточно, например, уважаемые французы имеют больше букв алфавита — диакритических знаков, как же их различать? Пришлось включить ещё одну высокую битовую позицию, таким образом, в Европе стало широко использоваться полубайтное кодирование, которое может представлять до 256 символов. Европейцы любят всё делать прямо и честно, поэтому при меньшем количестве символов используется меньше бит.3. Однако даже с меньшим количеством бит, различные страны и регионы используют свои собственные кодировки символов. Хотя символы от 0 до 127 одинаковы, интерпретация символов от 128 до 255 совершенно хаотична. Даже если двоичное представление абсолютно такое же, символы могут быть совершенно разными. Например, число 135 в кодировках французского, иврита и русского языка представляет собой совершенно разные символы.4. Более того, когда эта компьютерная технология попала в Китай, китайцы обнаружили, что у них есть более 100 тысяч различных иероглифов, а ваши 256 символов просто не подходят. Поэтому были созданы кодировки иероглифов, такие как GB2312, где большинство часто используемых иероглифов представлено двумя байтами, что позволяет представлять до 65536 иероглифов. Это объясняет, почему некоторые иероглифы можно найти в словарях, но они не будут отображены на компьютерах без специальной обработки.
5. Теперь каждый использует свои наборы символов для кодировки, как же это можно объединить? Русский отправляет электронное письмо китайцу, а их наборы символов различаются, и естественно, всё отображается как мешанина символов. Чтобы решить эту проблему, был придуман Unicode, который включает все символы мира, каждому символу присваивается уникальный код. Сейчас Unicode может содержать более миллиона символов, каждый с уникальным кодом. Это позволило всем языкам общаться друг с другом, теперь одна веб-страница может одновременно отображать текст на разных языках.6. Однако, хотя Unicode и унифицировал двоичное представление всех символов мира, он не установил правила хранения. В x86 и AMD архитектурах компьютеры используют малый и большой конец, что затрудняет понимание компьютерами, является ли данный символ частью Unicode или ASCII. Если бы Unicode установил правило использования трёх или четырёх байтов для каждого символа, то перед каждым английским символом обязательно были бы два или три нулевых байта, что увеличило бы размер текстового файла вдвое или втрое, что было бы крайне неэффективно для хранения данных. Это привело к тому, что появились различные способы хранения Unicode.
7. С развитием интернета, чтобы отображать различные символы на веб-страницах, требовалось единство. UTF-8 является одним из самых важных способов реализации Unicode. Также существуют UTF-16, UTF-32 и другие. UTF-8 не имеет фиксированной длины кодирования, а представляет собой переменную длину кодирования. Он может использовать от одного до четырёх байт для представления одного символа, меняя длину байтов в зависимости от символа. Это довольно изящное решение, так как если первый байт начинается с нуля, этот байт представляет собой отдельный символ; если первый байт начинается с единицы, количество единиц показывает сколько байтов используется для текущего символа.8. Обратите внимание, что кодировка символов Unicode и метод хранения UTF-8 различаются. Например, код Unicode для символа "严" равен 4E25, а его кодировка UTF-8 — E4B8A5. В разделе 7 объясняется, что UTF-8 не только кодирует символы, но и обеспечивает их хранение, таким образом, E4B8A5 представляет собой код 4E25 после распознавания кодировки.
9. UTF-8 использует от одного до четырёх байт для кодирования каждого символа. 128 ASCII символов (диапазон Unicode от U+0000 до U+007F) требуют один байт, латинские символы с диакритическими знаками, греческие буквы, кириллица, армянский алфавит, иврит, арабский алфавит, сирийский алфавит и мальдивский алфавит (диапазон Unicode от U+0080 до U+07FF) требуют двух байт, остальные символы базовой многоязычной плоскости (BMP), такие как CJK (диапазон Unicode от U+0800 до U+D7FF и U+E000 до U+FFFF), требуют трёх байт, а символы дополнительной многоязычной плоскости используют четыре байта для кодирования. Таким образом, мы можем проверить, какую кодировку используют в системе HarmonyOS, создав демонстрационный пример.
```c
void test(void) {
uint8_t i = 0;
char *ch = "鸿蒙";
//uint32_t byte;
printf("Длина строки: %d\r\n", strlen(ch));
for (i = 0; i < strlen(ch); i++) {
printf("Код символа: %x \n", *(ch + i));
}
}
Выходные данные:
Длина строки: 6
Код символа: ffffffe9
Код символа: ffffffb8
Код символа: ffffffbf
Код символа: ffffffe8
Код символа: ffffff92
Код символа: ffffff99
```Полученные значения `e9b8bf` и `e89299` показывают, что каждый китайский символ представлен шестью байтами, а значит, используется UTF-8.
Хорошо, теперь когда мы знаем используемую кодировку, можно переходить к следующему шагу. Устройство SSD1306 не имеет встроенной таблицы символов, поэтому нам потребуется сгенерировать её самостоятельно. Обычно это делается путём преобразования нужного шрифта в массив данных, который затем используется для отображения символов. Мы будем использовать аналогичный подход, но будем находить символы по их кодовым значениям.
Сначала приведём китайские символы к UTF-8 кодировке. Например, кодировка слова "鸿蒙" будет `0xe9b8bf 0xe89299`.
Преобразование осуществляется через сайт [конвертация UTF-8](https://www.qqxiuzi.cn/bianma/Unicode-UTF.php).
Затем создаём структуру данных:
```c
typedef struct {
unsigned int Index; // Код символа в UTF-8
unsigned char Msk[32]; // Шаблон символа
} typFNT_GB16;
Создаём массив структур:
typFNT_GB16 CN16_Msk[2] = {
{
0xE9B8BF,
{
0x00,0x80,0x40,0x1F,0x84,0x44,0x44,0x04,0x24,0x44,0xC4,0x47,0x5C,0x48,0x40,0x00,
0x10,0x20,0x7C,0x44,0x64,0x54,0x44,0x4C,0x40,0x7E,0x02,0x02,0x7A,0x02,0x0A,0x04, /* "鸿", 0 */
},
},
{
0xE89299,
{
0x08,0xFF,0x08,0x7F,0x40,0x8F,0x00,0x7F,0x06,0x3B,0x04,0x19,0x62,0x0C,0x72,0x01,
0x20,0xFE,0x20,0xFE,0x02,0xE4,0x00,0xFC,0x00,0x08,0xB0,0xC0,0xA0,0x98,0x86,0x00, /* "蒙", 1 */
},
},
};
Шаблоны генерируются при помощи программы PCtoLCD, конфигурация — негативный шаблон, положительное направление, матрица пикселей, 16-ричная система.## Код для отображения китайских символов
После выполнения всех подготовительных работ, можно приступить к написанию функций для отображения символов. Вначале планировалось использовать встроенный массив данных устройства, однако автор использовал тип данных u16, который несовместим с нашими требованиями, поэтому придётся реализовать свою версию. Вот пример кода:```c // Пример функции для отображения символа void display_char(unsigned int code) { // Поиск кода символа в массиве структур for(int i = 0; i < sizeof(CN16_Msk)/sizeof(typFNT_GB16); i++) { if(code == CN16_Msk[i].Index) { // Отображение символа for(int j = 0; j < 32; j++) { // Логика отображения символа } return; } } // Обработка случая, если символ не найден }
```c
void ssd1306_Print(uint8_t x, uint8_t y, char *s, SSD1306_COLOR color)
{
unsigned char i, k, length;
uint32_t Index = 0;
uint8_t b;
length = strlen(s); // Получение полной длины строки
for (k = 0; k < length; k++)
{
if ((uint8_t)(*(s + k)) <= 127) // Если значение меньше или равно 127, это ASCII символ
{
ssd1306_SetCursor(x, y);
ssd1306_DrawChar_u8(*(s + k), color);
x += 8; // Увеличение координаты x на 8
}
else if ((uint8_t)(*(s + k)) > 127) // Если значение больше 127, это китайский символ UTF-8 (3 байта)
{
Index = ((uint8_t)(*(s + k)) << 16) | ((uint8_t)(*(s + k + 1)) << 8) | (uint8_t)((s + k + 2));
// Получение кодировки китайского символа
for (i = 0; i < sizeof(CN16_Msk) / 34; i++) // Поиск в массиве
{
if (Index == CN16_Msk[i].Index)
{
for (uint8_t t = 0; t < 2; t++)
{
for (uint8_t k = (0 + 16 * t); k < (16 + 16 * t); k++) { //
b = CN16_Msk[i].Msk[k];
for (uint8_t j = 0; j < 8; j++)
{
if ((b << j) & 0x80)
{
ssd1306_DrawPixel(x + j, (y + k % 16), color);
}
else
{
ssd1306_DrawPixel(x + j, (y + k % 16), !color);
}
}
x += 8;
}
}
k += 2; // Каждый китайский символ занимает 3 байта, пропустить следующие 2 байта
}
}
}
}
}
```### Поддержка английских символов
Для поддержки английских символов, базовый шрифт не содержит массива символов размером 8x16 для английских букв, самый близкий размер — 7x10. Поэтому я создал функцию для отображения символов размером 8x16:
---
```markdown
# Поддержка английских символов
Для поддержки английских символов, базовый шрифт не содержит массива символов размером 8x16 для английских букв, самый близкий размер — 7x10. Поэтому я создал функцию для отображения символов размером 8x16:
char ssd1306_Отрисовка_Буквы_u8(char ch, SSD1306_COLOR color) {
uint32_t i, j;
uint8_t b;
// Проверка валидности символа
if (ch < 32 || ch > 126)
return 0;
// Проверка оставшегося пространства на текущей линии
if (SSD1306_WIDTH < (SSD1306.CurrentX + 8) ||
SSD1306_HEIGHT < (SSD1306.CurrentY + 16)) {
// Недостаточно места на текущей линии
return 0;
}
// Использование шрифта для отрисовки
for(i = 0; i < 16; i++) {
b = Font8x16[(ch - 32) * 16 + i];
for(j = 0; j < 8; j++) {
if ((b << j) & 0x80) {
ssd1306_Отрисовка_Пикселя(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);
} else {
ssd1306_Отрисовка_Пикселя(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);
}
}
}
// Текущее место занято
SSD1306.CurrentX += 8;
}
Этот метод предназначен для работы с ASCII-кодами размером 8x16. Если вам требуется использовать другой размер, внесите соответствующие изменения самостоятельно.
```Если вы хотите использовать нативный шрифт, просто замените `ssd1306_DrawChar_u8(*(s+k), color);` на `ssd1306_DrawChar((s+k), (шрифт), color);`.
Проверка
---------
После написания кода следует этап проверки. Результат проверки представлен на приведённой выше картинке. Написание такого кода действительно делает его использование очень удобным.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )