L’objectif est de mettre en oeuvre une communication série utilisant la liaison RS232 de la carte Atmel SAM4S.
Remarque : Pour ce TP, vous aurez besoin de relier un adaptateur USB/RS232 sur le port série UART J7 et d’utiliser un logiciel d’émulation de terminal ($ picocom -b 115200 /dev/ttyUSB0
ou cutecom
sous GNU/Linux ou TeraTerm
sous Windows) sur le port série virtuel. On quitte picocom
en utilisant la combinaison de touches Ctrl-A
puis Ctrl-X
.
La transmission série est un mode de transmission de données dans lequel les éléments d’information se succèdent les uns après les autres sur une seule voie entre deux points. Elle s’oppose à la transmission parallèle, qui transmet simultanément les éléments d’information sur plusieurs voies.
La transmission série domine dès que les composants ou périphériques à relier sont à quelque distance. L’ensemble des télécommunications s’établit sur des liaisons série.
La transmission série est très présente dans le monde industriel :
pour relier des capteurs/actionneurs (sensor bus) ou des composants de bas niveau, on utilise des technologies comme le bus TWI/I2C, 1-Wire, AS-i, …
pour relier des périphériques (appareils divers, système de commande, …), on utilise des bus de terrain (field bus) comme le bus CAN, DMX, les liaisons RS232 ou RS485, …
La transmission série se fait généralement par trames et nécessite la mise en oeuvre de protocole.
Remarque : Communiquer consiste à transmettre des informations, mais tant que les interlocuteurs ne lui ont pas attribué un sens, il ne s’agit que de données et pas d’information. Les interlocuteurs doivent donc non seulement parler un langage commun mais aussi maîtriser des règles minimales d’émission et de réception des données. C’est le rôle d’un protocole de s’assurer de tout cela.
La carte Atmel SAM4S dispose de deux circuits de transmission série :
Documentation : Chapitre 35 Universal Asynchronous Receiver Transmitter (UART) page 761 du atmel-11100-32-bit-cortex-m4-microcontroller-sam4s_datasheet.pdf et Chapitre 36 Universal Synchronous Asynchronous Receiver Transceiver (USART) page 779 du atmel-11100-32-bit-cortex-m4-microcontroller-sam4s_datasheet.pdf
Pour simplifier la programmation directe de l’UART, la bibliothèque logicielle ASF fournit des fonctions de contrôle : http://asf.atmel.com/docs/latest/sam4s/html/group__sam__drivers__uart__group.html et http://asf.atmel.com/docs/latest/sam4s/html/group__sam__drivers__usart__group.html.
On a déjà utilisé la liaison série RS232 (UART) à des fins d’affichage de messages de débugage (fonctions puts()
et printf()
) :
En effet les fonctions de la stdio
utilisent les fonctions de bas niveau read()
et write()
qui ont été réécrites pour recevoir ou envoyer des caractères sur le port série (cf. /usr/local/asf/common/utils/stdio/
).
On peut donc soit utiliser des fonctions standards (puts()
ou printf()
et scanf()
) ou celles de l’API UART pour émettre et recevoir des caractères :
uart_read()
et uart_write()
uart_is_rx_ready()
et uart_is_tx_ready()
On crée ce programme de test :
$ vim main.c
#include <asf.h>
/** UART Interface */
#define CONF_UART CONSOLE_UART
#define CONF_UART_BAUDRATE 115200
#define CONF_UART_PARITY UART_MR_PAR_NO
#define CONF_UART_CHAR_LENGTH US_MR_CHRL_8_BIT
#define CONF_UART_STOP_BITS US_MR_NBSTOP_1_BIT
int main (void)
{
board_init();
sysclk_init();
const usart_serial_options_t uart_serial_options =
{
.baudrate = CONF_UART_BAUDRATE,
.charlength = CONF_UART_CHAR_LENGTH,
.paritytype = CONF_UART_PARITY,
.stopbits = CONF_UART_STOP_BITS,
};
sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
stdio_serial_init(CONF_UART, &uart_serial_options);
uint8_t fini = 0;
uint8_t prompt[] = "\r> ";
uint8_t touche = 0;
printf("%s", (char *)prompt);
// echo et touche Entree pour quitter
while(!fini)
{
scanf("%c", &touche);
if (touche == '\r')
{
puts("\r");
fini = 1;
}
else
{
printf("%c", touche);
}
}
puts("End.\r\n");
return 0;
}
Code source : tp3-uart.zip
Test :
$ make
$ ./run.sh xxx_flash.elf
À vous de jouer :
Tester la première version du programme.
Écrire une version utilisant les fonctions de l’API UART pour effectuer un echo des caractères reçus.
La carte Atmel SAM4S fournit un circuit USART (Universal Synchronous Asynchronous Receiver Transceiver) qui permet de gérer les modes de fonctionnement des interfaces sur RS485, bus SPI, carte à puce ISO 7816, émetteurs-récepteurs infrarouges et connexion aux ports de modem RS232.
Ici, on va utiliser le port UART1 en mode standard RS232 physiquement disponible sur le connecteur DB9 du kit de développement SAM4S-EK.
On utilisera les lignes TXD1 (PA22) et RXD1 (PA21_232) sans contrôle de flux matériel (RTS/CTS).
Attention : il faudra activer par un niveau bas la ligne /EN (PA23) et placer le cavalier JP31 en position 1-2.
Pour cette manipulation, il vous faudra relier un adaptateur USB/RS232 sur le port série USART J5 et utiliser un logiciel d’émulation de terminal (picocom
ou cutecom
sous GNU/Linux ou TeraTerm
sous Windows) sur le port série virtuel. On quitte picocom
en utilisant la combinaison de touches Ctrl-A
puis Ctrl-X
.
On commencera par configurer les lignes du PIOA :
gpio_configure_pin(PIN_USART1_RXD_IDX, PIN_USART1_RXD_FLAGS);
gpio_configure_pin(PIN_USART1_TXD_IDX, PIN_USART1_TXD_FLAGS);
gpio_configure_pin(PIN_USART1_EN_IDX, PIN_USART1_EN_FLAGS);
gpio_set_pin_low(PIN_USART1_EN_IDX);
Puis on initialisera le port UART1 pour une communication RS232 :
#define CONF_USART_BAUDRATE 115200
const sam_usart_opt_t usart_options = {
CONF_USART_BAUDRATE,
US_MR_CHRL_8_BIT,
US_MR_PAR_NO,
US_MR_NBSTOP_1_BIT,
US_MR_CHMODE_NORMAL,
0 // ce champ n'est utile qu'en mode IrDA
};
sysclk_enable_peripheral_clock(ID_USART1);
// Configure l'USART
usart_init_rs232(USART1, &usart_options, sysclk_get_cpu_hz());
Remarque : les paramètres de configuration sont définis dans le fichier /usr/local/asf/sam/utils/cmsis/sam4s/include/component/component_uart.h
. Les fonctions et structures utilisées sont déclarées dans le fichier /usr/local/asf/sam/drivers/usart/usart.h
.
Ensuite, il suffit d’utiliser des fonctions de l’API USART pour émettre et recevoir des caractères :
usart_putchar()
et usart_getchar()
usart_write()
, usart_write_line()
et usart_read()
usart_serial_write_packet()
et usart_serial_read_packet()
On écrit un premier programme de test du port USART1 :
$ vim main.c
#include <asf.h>
#include <string.h>
/** UART Interface */
#define CONF_UART CONSOLE_UART
#define CONF_UART_BAUDRATE 115200
#define CONF_UART_PARITY UART_MR_PAR_NO
#define CONF_UART_CHAR_LENGTH US_MR_CHRL_8_BIT
#define CONF_UART_STOP_BITS US_MR_NBSTOP_1_BIT
/** USART Interface */
#define CONF_USART USART1
/**
* Configure UART.
*/
static void configure_uart(void)
{
const usart_serial_options_t uart_serial_options =
{
.baudrate = CONF_UART_BAUDRATE,
.charlength = CONF_UART_CHAR_LENGTH,
.paritytype = CONF_UART_PARITY,
.stopbits = CONF_UART_STOP_BITS,
};
sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
stdio_serial_init(CONF_UART, &uart_serial_options);
}
/**
* Configure USART1 RS232.
*/
static void configure_usart_rs232(void)
{
const sam_usart_opt_t usart_options = {
CONF_USART_BAUDRATE,
US_MR_CHRL_8_BIT,
US_MR_PAR_NO,
US_MR_NBSTOP_1_BIT,
US_MR_CHMODE_NORMAL,
0 // ce champ n'est utile qu'en mode IrDA
};
sysclk_enable_peripheral_clock(ID_USART1);
// Configure l'USART
usart_init_rs232(USART1, &usart_options, sysclk_get_cpu_hz());
}
int main (void)
{
board_init();
/* Configure USART RXD pin & TXD pin */
gpio_configure_pin(PIN_USART1_RXD_IDX, PIN_USART1_RXD_FLAGS);
gpio_configure_pin(PIN_USART1_TXD_IDX, PIN_USART1_TXD_FLAGS);
gpio_configure_pin(PIN_USART1_EN_IDX, PIN_USART1_EN_FLAGS);
gpio_set_pin_low(PIN_USART1_EN_IDX);
sysclk_init();
/* Initialise le port uart */
configure_uart();
/* Initialise le port usart */
configure_usart_rs232();
puts("Test1 RS232\r\n");
uint32_t data = '$';
uint32_t status;
status = usart_write(USART1, data);
//printf("usart_write() %ld\r\n", status);
usart_write_line(USART1, "Ok");
usart_write_line(USART1, "\r\n");
puts("Test2 RS232\r\n");
uint8_t fini = 0;
uint8_t tx_len = 0;
uint8_t tx_buf[] = "\n\rHello world ! : ";
uint8_t rx_buf[] = "0";
tx_len = (uint8_t)strlen((char *)tx_buf);
usart_serial_write_packet(USART1, tx_buf, tx_len);
// echo et touche Entree pour quitter
while(!fini)
{
usart_serial_read_packet(USART1, rx_buf, 1);
if (rx_buf[0] == '\r')
{
usart_serial_write_packet(USART1, tx_buf, 2);
fini = 1;
}
else
{
printf("echo_usart() read : %c 0x%02X\r\n", rx_buf[0], rx_buf[0]);
usart_serial_write_packet(USART1, rx_buf, 1);
}
}
puts("End.\r\n");
return 0;
}
Pour la réception de caractères, il semble judicieux d’utiliser un fonctionnement par interruption.
Pour cela à l’initialisation, on réalisera en plus la séquence suivante :
#define ALL_INTERRUPT_MASK 0xffffffff
// Désactive toutes les interruptions
usart_disable_interrupt(USART1, ALL_INTERRUPT_MASK);
// Active l'émission et la réception
usart_enable_tx(USART1);
usart_enable_rx(USART1);
// Active les interruptions sur l'USART
NVIC_EnableIRQ(USART1_IRQn);
// Active l'interruption RXRDY
usart_enable_interrupt(USART1, US_IER_RXRDY);
Il suffit ensuite de définir le gestionnaire d’interruption USART1_Handler()
:
static uint32_t read_buffer = 0;
void USART1_Handler(void)
{
uint32_t ul_status;
// Lit l'état de l'USART
ul_status = usart_get_status(USART1);
//printf("status : 0x%04X\r\n", (uint8_t)ul_status);
// caractère reçu ?
if (ul_status & US_CSR_RXRDY)
{
// Lit le caractère
usart_getchar(USART1, (uint32_t *)&read_buffer);
}
}
Il peut être utile de réinitialiser les buffers Tx et Rx comme ceci :
// Désactive l'interruption RXBUFF
usart_disable_interrupt(USART1, US_IDR_RXBUFF);
// Réinitialise TX et RX
usart_reset_rx(USART1);
usart_reset_tx(USART1);
// Active TX et RX
usart_enable_tx(USART1);
usart_enable_rx(USART1);
// Active l'interruption RXRDY
usart_enable_interrupt(USART1, US_IER_RXRDY);
Code source : tp3-usart.zip
Test :
$ make
$ ./run.sh xxx_flash.elf
À vous de jouer :
Tester la première version du programme.
Configurer maintenant le port en 9600 bits/s et écrire une version utilisant les interruptions pour effectuer un echo des caractères reçus.
Vous devez être capable de répondre aux questions suivantes :
Quels sont les paramètres de configuration d’un port série ?
Quel est le rôle du bit de START ?
Quel est le rôle du bit de parité ?
Quelle est la valeur du bit de parité paire pour la séquence suivante : 01100000 ?
Rechercher la signification de baud.
Quel est le rôle d’un contrôle de flux ?
Citer les avantages et inconvénients des contrôle de flux RTS/CTS et Xon/Xoff.
Calculer la durée de transmission d’un caractère sur 8 bits avec la configuration suivante : 9600 bits/s, pas de parité et 1 bit de stop. Calculer alors la durée de transmission d’une trame de 769 caractères.
Comparer les normes RS232 et RS485.
Citer plusieurs technologies de bus série.
^
en langage C.