Кто любит RISC в жизни, заходим, не стесняемся.
Вс дек 25, 2016 17:55:24
Здравствуйте.
Недавно занялся процессорами stm32, и вот, хочу поспрашивать про библиотеку LibC.
Портирую проект с OrangePI(клон Raspberry), который ранее работал очень медленно и на AVR.
В предыдущих версиях проектов я работал с TFT дисплеем, малельким дисплейчиком, устройствами i2c, uart и консолью через файлы.
На Linux работать с файлами элементарно, и на AVR библиотека LIBC тоже это позволяла без проблем.
А на stm32 этот момент как то плохо описан, максимум попадается "прибитие гвоздями" uart к printf.
Хочется перенести код типа такого:
Спойлер
- Код:
//создаются файлы для работы с устройствами
FILE *uart_stream;
FILE *log_stream;
FILE *lcd_info_stream;
FILE *tft_console_stream;
...
//есть фунукции работы с железом
extern int uart0_putchar(char c, FILE *stream __attribute__ ((__unused__)) );
extern int tft_putchar(char c, FILE *stream __attribute__ ((__unused__)) );
extern int tft_console_putchar(char c, FILE *stream __attribute__ ((__unused__)) );
...
//связываем файловые потоки с "железом" (это пример для AVR, а на linux открываем с помощью fopen файлы устройств)
uart_stream = fdevopen(uart0_putchar, uart0_getcharL); //send , receive functions
tft_stream = fdevopen(tft_putchar, NULL); //send , receive functions
tft_console_stream = fdevopen(tft_console_putchar, keyboard_getchar); //send , receive functions
...
//далее работаем с разными устройствами. для примера, пусть будет libc.printf
fprintf(log_stream,"some var=%6u",v);
fprintf(tft_console_stream,"\e[31mText..");
//при работе с файлами на AVR приходится дополнительно связать структуру stdio.FILE со структурой "FILE" из библиотеки для работы с SD картой. На Lunux лишние действия не требуются.
Рассмотрев библиотеку
LIBC для STM32, в упор не заметил работающую структуру
FILE.
И не обнаружил примеров работающих с stdio.FILE.
(Кроме примеров как жестко "повесить" printf на Uart через переопределение функции _write/putchar)
А вот примеров создания нескольких потоков FILE для ввода/вывода не нашел.
Да, можно сделать ветвление в функции _write, но точно я не буду мешать все устройства и файловые операции придачу в одной функции.
Писать костыли под такую LIBC как то нет желания. Как бы не легче было другую libc прикрутить.
Надеюсь, я просто чего то не заметил. Или эта задача делается на stm32 иначе.
Подскажите как правильно делаются потоки ввода вывода на stm32.
Вс дек 25, 2016 19:27:09
так вроде бы потоки так и делаются, а вот файловая система - за вами.
я боюсь ошибиться, но по-моему товарищ Чен давно эту проблему решил:
http://elm-chan.org/fsw/ff/00index_e.html
Пн дек 26, 2016 17:02:12
Да, библиотеки товарища Чена рабочие, что то я даже использовал. Но проблему с потоками не решал, боле того он их не использовал вовсе.
Обычно, имеющиеся у потока/файла функции чтения, записи и прочие, описаны или в самой структуре или сслылаются на другую структуру устройства(или на драйвер).
Всё ,больше о работе с железом знать никому не надо, и библиотеке stdio особенно.
В структуре FILE библиотеки LIBC определены поля _read, _write, но оно не работает, и ни примеров ни описаний.
Ср дек 28, 2016 02:13:51
_kp писал(а):Да, можно сделать ветвление в функции _write, но точно я не буду мешать все устройства и файловые операции придачу в одной функции.
Ну вот, ядро за вас делает а вы не хотите! Но write(fd) и read(fd) хотите. Не бывает так (UNIX: все есть файл)
_kp писал(а):a
В структуре FILE библиотеки LIBC определены поля _read, _write, но оно не работает, и ни примеров ни описаний.
А что вы в примере ожидаете увидеть?
Посмотрите на реализацию методов в ядре (linux kernel).
Ничего там военного нет, вам нужно реализовать несколько syscall'ов. _open, _close, _seek, _write, _read, может быть _flush. и все. Пример с "прибиванием гвоздями printf" вполне релевантен. Он работает с одним fd (скорее всего в вашем в случае примеров что вы видели - тупо забивает на него), вам же нужно расширить поддержку до кастомных дескрипторов связанных с вашей FS.
PS: fd - file descriptor, если вы не знаете что это, задача съест много вашего времени
Ср дек 28, 2016 05:16:46
Нашел как работать с потоками на stm32!
Открытие потока со своими функциями делается функцией - fopencookie()
Практически, расширенный аналог, того что на avr делала функция fdevopen()
Когда разобрался, то и небольшое количество примеров нашлось, по ключевому слову fopencookie , а в стандартных примерах, почему то вовсе не упоминается столь важная функция.
Итак, решение:
//Создаём функции обычного блочного чтения/записи для работы с железом, но тип аргументов должен совпадать с требуемым в stdio.h
//В параметре _cookie можно передать произвольный аргумент, например для однотипных функций, работающих с разным железом (я не использую)
ssize_t uart1_write( void *_cookie, const char *buffer, size_t length);
ssize_t uart1_read(void *_cookie, char *buffer, size_t length);
ssize_t tft_write( void *_cookie, const char *buffer, size_t length);
ssize_t tft_cons_write( void *_cookie, const char *buffer, size_t length);
ssize_t tft_cons_read( void *_cookie, char *buffer, size_t length);
//Теперь главное
//Объявляем файловые потоки и связываеи их с железом
uart1_init(); //Инициализируем железо перед чтением/записью
FILE f_uart1 = fopencookie( NULL, "rw+", (cookie_io_functions_t){ uart1_read, uart1_write, NULL, NULL } ); //создаём поток
setvbuf(f_uart1 , NULL, _IONBF, 0); //отключем буферизацию (если требуется)
tft_init();
FILE f_tft = fopencookie( NULL, "w+", (cookie_io_functions_t){ NULL, tft_write , NULL, NULL } ); //только режим записи
setvbuf(f_tft , NULL, _IONBF, 0);
tft_console_init();
FILE f_tft_console = fopencookie( NULL, "rw+", (cookie_io_functions_t){ tft_cons_read, tft_cons_write, NULL, NULL } );
//Готово. Можно использовать откуда угодно через потоки.
fputs(f_uart1 ,"Text to uart");
fputs(f_tft,"Text to console");
fprintf(tft_console,"\e[%u;%uH\e[%sm%s",y,x,dattr,str );
Примечание. C помощью setvbuf() можно отключить буферизацию на уровне библиотеки stdio.
Та буферизация не альтернатива буферизции с помощью DMA/прерываний, а для многопоточного кода.
Кроме того, она абсолютно вредна для нетекстовых данных, и быстодействияя не прибавляет, и даже наооборот уменьшает.
Но она полезна при записи в один поток данных из многопоточного кода(и только многопоточного), например ведение лог-файла, и
при использовании буферизации данные будут смешаны не побайтно, а постройно, то есть будут читаемы.
ps: Потоки хороши тем, что их можно перенаправить, в отличие от жестких функций работы с железом.
Ср мар 25, 2020 07:24:37
#include <stdio.h>
ssize_t myWrite(void *cookie, const char *buf, size_t n)
// wrapper function
{
return Serial.write((uint8_t*)buf, n);
}
cookie_io_functions_t myVectors = { 0, myWrite, 0, 0 };
FILE *myOut;
void setup() {
Serial.begin(9600);
myOut = fopencookie((void *)(&Serial), "w", myVectors);
setlinebuf(myOut);
fprintf(myOut, "This is an fprintf demo\n");
}
void loop(void)
{
fprintf(myOut,"frintf\n\r");
delay(3000);
}