Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

stm32 libc FILE

Вс дек 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.

Re: stm32 libc FILE

Вс дек 25, 2016 19:27:09

так вроде бы потоки так и делаются, а вот файловая система - за вами.

я боюсь ошибиться, но по-моему товарищ Чен давно эту проблему решил: http://elm-chan.org/fsw/ff/00index_e.html

Re: stm32 libc FILE

Пн дек 26, 2016 17:02:12

Да, библиотеки товарища Чена рабочие, что то я даже использовал. Но проблему с потоками не решал, боле того он их не использовал вовсе.

Обычно, имеющиеся у потока/файла функции чтения, записи и прочие, описаны или в самой структуре или сслылаются на другую структуру устройства(или на драйвер).
Всё ,больше о работе с железом знать никому не надо, и библиотеке stdio особенно.

В структуре FILE библиотеки LIBC определены поля _read, _write, но оно не работает, и ни примеров ни описаний.

Re: stm32 libc FILE

Ср дек 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, если вы не знаете что это, задача съест много вашего времени

Re: stm32 libc FILE

Ср дек 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: Потоки хороши тем, что их можно перенаправить, в отличие от жестких функций работы с железом.

как работать с потоками на stm32, рабочий пример

Ср мар 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);

}
Ответить