W tym poście chciałbym opisać sposób obsługi interfejsu UART.
[Źródło: https://somlabs.com/product/visionsom-6ull/]
Device Tree:
Dla testowanego programu uruchomiłem interfejs UART3. W tym celu należy wprowadzić następujące zmiany w Device Tree:
- &uart4 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_uart4>;
- dma-names = "", "";
- status = "okay";
- };
- pinctrl_uart4: uart3grp {
- fsl,pins = <
- MX6UL_PAD_UART4_TX_DATA__UART4_DCE_TX 0x1b0b1
- MX6UL_PAD_UART4_RX_DATA__UART4_DCE_RX 0x1b0b1
- >;
- };
Podobnie będzie wyglądała konfiguracja pinów np. dla UART3. Natomiast z płytki VisonCB-STD linie RX i TX dla UART3 nie zostały wyprowadzone na złącza.
W przypadku wykorzystywania linii UART4 RX oraz TX należy zakomentować deklarację I2C1. Ponieważ oba interfejsy korzystają z tych samych wyprowadzeń.
Linie UART4 zostały wyprowadzone na złącze do nakładek Arduino (żółte 8 pinów):
Konwerter poziomów logicznych w standardowej konfiguracji nie zmienia sygnałów na 5V tylko utrzymuje poziom 3V3.
Minicom:
W celu wykonania testów połączenia można się posłużyć programem Minicom.
Najpierw jednak należy podłączyć konwerter UART->USB do linii AR-DIO-1 oraz AR-DIO-0 oraz GND (np. pin 6 złącza Raspberry Pi).
Przed uruchomieniem interfejsu należy zainstalować program Minicom:
- apt install minicom
Następnie w celu uruchomienia programu należy wpisać:
- minicom -D /dev/ttymxc3
Domyślne prędkość transmisji wynosi 115200, 8 bitów danych, brak kontroli parzystości oraz jeden bit stopu.
Po przesłanie danych z komputera do płytki Colibri powinny się one wyświetlić w następujący w oknie:
Aby wysłać dane należy je wpisać w oknie. Dane przesyłane są pojedynczo:
Dodatkowe ustawienia programu dostępne są po wpisaniu kombinacji CTRL + A po czym klawisz Z.
Aby wyłączyć program Minicom należy wpisać CTRL+A, Z oraz X;
Program C:
Aby uruchomić przykładowy program można wykorzystać informację udostępnione przez producenta pod tym linkiem: https://wiki.somlabs.com/index.php/How_to_use_VisionSOM-6ULL_UART_interface_in_command_line_and_C_programs
Program napisany w języku C pozwala na przesłanie oraz odbiór danych z prędkością 9600.
Poniżej rozwinę trochę powyższy przykład.
Konfiguracja UART'a:
- int configureUART(int fd, int speed)
- {
- struct termios serialPortSett;
- memset (&serialPortSett, 0, sizeof serialPortSett);
- /* Kopiowanie ustawień */
- tcgetattr (fd, &serialPortSett);
- /* Ustawienie prędkości odczytu UART */
- cfsetispeed (&serialPortSett, speed);
- /* Ustawienie prędkości zapisu UART */
- cfsetospeed (&serialPortSett, speed); //Ustaw prędkość zapisu UART
- /* Disables parity */
- serialPortSett.c_cflag &= ~(PARENB | PARODD);
- /* CSTOPB oznacza dwa bity stopu tutaj zanegowane
- czyli ustawiony jeden bit stopu*/
- serialPortSett.c_cflag &= ~CSTOPB;
- /* Czyszczenie maski do zapisu rozmiaru danych */
- serialPortSett.c_cflag &= ~CSIZE;
- /* Ustawienie rozmiaru danych na 8 */
- serialPortSett.c_cflag |= CS8;
- /* Brak sprzętowej kontroli przepływu danych */
- serialPortSett.c_cflag &= ~CRTSCTS;
- /* Uruchomienie odbierania
- Wyłączenie ang. modem control lines */
- serialPortSett.c_cflag |= CREAD | CLOCAL;
- /* Wyłączenie XON/XOFF */
- serialPortSett.c_iflag &= ~(IXON | IXOFF | IXANY);
- /* Wyłączenie trybu kanonicznego oraz echa */
- serialPortSett.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- /* Output processing wyłączony */
- serialPortSett.c_oflag &= ~OPOST;
- /* Timeout */
- serialPortSett.c_cc[VMIN] = 0;
- serialPortSett.c_cc[VTIME] = 0;
- /* Ustawienie danych */
- if((tcsetattr(fd,TCSANOW,&serialPortSett)) != 0) {
- printf("\n UART init ERROR");
- }
- else{
- printf("\n UART init OK");
- }
- /* Usunięcie danych z bufora RX */
- tcflush(fd, TCIFLUSH);
- }
Jako parametr speed czyli szybkość komunikacji można podać następujące wartości:
- B0
- B50
- B75
- B110
- B134
- B150
- B200
- B300
- B600
- B1200
- B1800
- B2400
- B4800
- B9600
- B19200
- B38400
- B57600
- B115200
- B230400
Poniżej cały program:
- #include <stdio.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <string.h>
- #include <termios.h>
- #include <unistd.h>
- int confUART(int fd, int speed)
- {
- struct termios serialPortSett;
- memset (&serialPortSett, 0, sizeof serialPortSett);
- tcgetattr (fd, &serialPortSett);
- cfsetispeed (&serialPortSett, speed);
- cfsetospeed (&serialPortSett, speed);
- serialPortSett.c_cflag &= ~(PARENB | PARODD);
- serialPortSett.c_cflag &= ~CSTOPB;
- serialPortSett.c_cflag &= ~CSIZE;
- serialPortSett.c_cflag |= CS8;
- serialPortSett.c_cflag &= ~CRTSCTS;
- serialPortSett.c_cflag |= CREAD | CLOCAL;
- serialPortSett.c_iflag &= ~(IXON | IXOFF | IXANY);
- serialPortSett.c_iflag &= ~IGNBRK;
- serialPortSett.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- serialPortSett.c_oflag &= ~OPOST;
- serialPortSett.c_cc[VMIN] = 0;
- serialPortSett.c_cc[VTIME] = 0;
- if((tcsetattr(fd,TCSANOW,&serialPortSett)) != 0) {
- printf("\n UART init ERROR\n");
- }
- else{
- printf("\n UART init OK\n");
- }
- tcflush(fd, TCIFLUSH);
- }
- void main() {
- char buf [50];
- int bytesRead = 0;
- int fd = open ("/dev/ttymxc3", O_RDWR | O_NOCTTY | O_SYNC);
- confUART(fd, B115200);
- printf("Sending mesage...\n");
- sprintf(buf, "Test msg\n");
- write (fd, buf, strlen(buf));
- memset(buf, 0, sizeof(buf));
- printf("Receiving message...\n");
- while(1)
- {
- bytesRead = read(fd, buf, sizeof(buf));
- printf("ReadBytes: %d\n", bytesRead);
- printf("Msg: %s\n", buf);
- }
- }
W funkcji main uzyskujemy dostęp do interfejsu przez otwarcie pliku. Dołożone flagi oznaczają możliwość odczytu i zapisu, brak kontroli przez terminal oraz synchronizacja zapisu odczytu z pliku.
Następnie przesyłana jest informacja po której następuje wejście do pętli while która odbiera dane i wyrzuca je w terminalu w postaci ilości odebranych danych oraz samego bufora. Przesłana ramka musi być zakończona znakiem CR.
Dokładne informacje o znaczeniu poszczególnych flag wartości można pobrać tutaj:
http://man7.org/linux/man-pages/man3/termios.3.html