<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christophe Blaess</title>
	<atom:link href="http://www.blaess.fr/christophe/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.blaess.fr/christophe</link>
	<description>Ingénierie et formations sur Linux et les logiciels libres</description>
	<lastBuildDate>Thu, 17 May 2012 20:01:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Solutions temps réel sous Linux</title>
		<link>http://www.blaess.fr/christophe/2012/05/15/solutions-temps-reel-sous-linux/</link>
		<comments>http://www.blaess.fr/christophe/2012/05/15/solutions-temps-reel-sous-linux/#comments</comments>
		<pubDate>Tue, 15 May 2012 06:08:27 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Livres]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2214</guid>
		<description><![CDATA[Aujourd&#8217;hui arrive en librairie mon nouveau livre qui traite des solutions libres pour obtenir un comportement temps réel souple ou strict avec Linux. Pour plus de détails&#8230;]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/couverture.png"><img class="alignright size-full wp-image-2215" title="couverture" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/couverture.png" alt="" width="165" height="200" /></a>Aujourd&#8217;hui arrive en librairie mon nouveau livre qui traite des solutions libres pour obtenir un comportement temps réel souple ou strict avec Linux.</p>
<p style="text-align: justify;"><a title="Solutions temps réel sous Linux" href="http://www.blaess.fr/christophe/livres/solutions-temps-reel-sous-linux/">Pour plus de détails&#8230;</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/05/15/solutions-temps-reel-sous-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GPIO, Pandaboard et temps réel &#8211; 2 &#8211; sorties depuis l&#8217;espace kernel</title>
		<link>http://www.blaess.fr/christophe/2012/05/14/gpio-pandaboard-et-temps-reel-2-sorties-depuis-lespace-kernel/</link>
		<comments>http://www.blaess.fr/christophe/2012/05/14/gpio-pandaboard-et-temps-reel-2-sorties-depuis-lespace-kernel/#comments</comments>
		<pubDate>Mon, 14 May 2012 05:19:39 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Embarqué]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Microprocesseur]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2173</guid>
		<description><![CDATA[Nous avons observé dans l&#8217;article précédent comment programmer et commander depuis l&#8217;espace utilisateur les GPIO. Il s&#8217;agit, nous l&#8217;avons vu, de broches du microprocesseur que nous pouvons affecter au choix en entrée ou en sortie et sur lesquelles il est possible de lire ou d&#8217;écrire des valeurs électriques. Nous avons utilisé l&#8217;interface offerte par le [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-01.jpg"><img class="aligncenter size-full wp-image-2174" title="GPIO Pandaboard et temps-réel" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-01.jpg" alt="GPIO Pandaboard et temps-réel" width="500" height="670" /></a>Nous avons observé dans <a title="GPIO, Pandaboard et temps réel – 1 – Sorties depuis l’espace utilisateur" href="http://www.blaess.fr/christophe/2012/05/09/gpio-pandaboard-temps-reel-1/">l&#8217;article précédent</a> comment programmer et commander depuis l&#8217;espace utilisateur les <em>GPIO</em>. Il s&#8217;agit, nous l&#8217;avons vu, de broches du microprocesseur que nous pouvons affecter au choix en entrée ou en sortie et sur lesquelles il est possible de lire ou d&#8217;écrire des valeurs électriques.</p>
<p style="text-align: justify;">Nous avons utilisé l&#8217;interface offerte par le noyau à travers le pseudo-système de fichiers <code>/sys</code> pour écrire un oscillateur s&#8217;exécutant dans l&#8217;espace utilisateur. Nos premiers essais réalisés avec un script shell souffrait d&#8217;une granularité excessive (plusieurs millisecondes) dans la gestion du temps. Un programme en C offrait une bien meilleure résolution (de l&#8217;ordre de la centaine de microsecondes). Ordonnancé en temps partagé les fluctuations étaient parfois très importantes (plusieurs millisecondes au pire). En utilisant l&#8217;ordonnancement temps réel souple de Linux, les fluctuations étaient beaucoup plus limitées.</p>
<p style="text-align: justify;">Nous allons à présent réaliser les mêmes opérations depuis l&#8217;espace kernel, en écrivant d&#8217;abord des modules pour le noyau Linux standard puis en nous intéressant à un module pour Xenomai.</p>
<h1>Timer du noyau Linux vanilla</h1>
<p style="text-align: justify;">Le noyau Linux classique nous offre une interface simple pour programmer des actions différées, et éventuellement répétitives, les structures <code>timer_list</code>. Notre premier exemple est construit autour de ces structures. Il programme l&#8217;exécution périodique d&#8217;une fonction toutes les millisecondes. Cette fonction basculera une sortie GPIO comme nous l&#8217;avons fait dans l&#8217;article précédent, mais en s&#8217;appuyant sur l&#8217;API kernel <code>&lt;linux/gpio.h&gt;</code>.</p>
<pre><strong><a title="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-timer.c" href="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-timer.c" target="_blank">oscillateur-gpio-timer.c :</a> </strong>
#include &lt;linux/cdev.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/fs.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/sched.h&gt;
#include &lt;linux/version.h&gt;
#include &lt;asm/uaccess.h&gt;

static void timer_oscillateur(unsigned long);
static <strong>struct timer_list</strong> timer;

// 138 Broche 10 du port J3 (Expansion A) de la Pandaboard
#define GPIO_OSCILLATEUR 138

static int __init init_oscillateur (void)
{
  int err;

  if ((err = <strong>gpio_request</strong>(GPIO_OSCILLATEUR, THIS_MODULE-&gt;name)) != 0) {
    return err;
  }
  if ((err = <strong>gpio_direction_output</strong>(GPIO_OSCILLATEUR, 1)) != 0) {
    gpio_free(GPIO_OSCILLATEUR);
    return err;
  }
  <strong>init_timer</strong> (&amp; timer);
  <strong>timer.function</strong> = timer_oscillateur;
  timer.data = 0;
  <strong>timer.expires</strong> = jiffies + HZ/1000;
  <strong>add_timer</strong>(&amp; timer);

  return 0;
}

static void __exit exit_oscillateur (void)
{
  <strong>del_timer</strong>(&amp; timer);
  <strong>gpio_free</strong>(GPIO_OSCILLATEUR);
}

static void timer_oscillateur(unsigned long unused)
{
  static int value = 0;
  <strong>gpio_set_value</strong>(GPIO_OSCILLATEUR, value);
  value = 1 - value;
  mod_timer(&amp; timer, jiffies + HZ/1000);
}

module_init(init_oscillateur);
module_exit(exit_oscillateur);
MODULE_LICENSE("GPL");</pre>
<p style="text-align: justify;">Comme nous le voyons, la durée de ces timers s&#8217;expriment en <em>jiffies</em>, en <em>ticks</em> système, dont la fréquence est contenue dans la constante symbolique <code>HZ</code>. IL y a <code>HZ</code> ticks par seconde. En programmant un déclenchement dans <code>HZ/1000</code> ticks, on diffère donc l&#8217;action d&#8217;une milliseconde.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-13.jpg"><img class="aligncenter size-full wp-image-2181" title="Connecteur d'extension de la Pandaboard" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-13.jpg" alt="Connecteur d'extension de la Pandaboard" width="600" height="407" /></a></p>
<p style="text-align: justify;">Pour compiler ce code (et les modules suivants), il va falloir utiliser un fichier <a title="http://www.blaess.fr/christophe/files/article-2012-05-14/Makefile" href="http://www.blaess.fr/christophe/files/article-2012-05-14/Makefile" target="_blank"><code>Makefile</code></a> spécifique. Lors de l&#8217;invocation de la commande <code>make</code>, nous devrons indiquer:</p>
<ul>
<li style="text-align: justify;"><code><strong>ARCH</strong>=arm</code> pour la Pandaboard</li>
<li style="text-align: justify;"><code><strong>CROSS_COMPILE</strong>=</code> suivi du préfixe à employer pour utiliser la chaîne de compilation croisée</li>
<li style="text-align: justify;"><code><strong>KERNEL_DIR</strong></code>= suivi du chemin d&#8217;accès au répertoire contenant les sources du noyau de la Pandaboard</li>
</ul>
<p style="text-align: justify;">À titre d&#8217;exemple, sur mon poste l&#8217;invocation de <code>make</code> se fait ainsi:</p>
<pre>$ <strong>make ARCH=arm CROSS_COMPILE=~/cross-panda/usr/bin/arm-linux- KERNEL_DIR=~/Projets/Panda/linux-2.6.38.8</strong>
make -C /home/cpb/Projets/Panda/linux-2.6.38.8 SUBDIRS=/home/cpb/Documents/Livres/Articles/Blog/article-2012-05-14  modules
make[1]: entrant dans le répertoire « /home/cpb/Projets/Panda/linux-2.6.38.8 »
  CC [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-timer.o
  CC [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-hrtimer.o
  CC [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-rtdm.o
  Building modules, stage 2.
  MODPOST 3 modules
  CC      /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-hrtimer.mod.o
  LD [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-hrtimer.ko
  CC      /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-rtdm.mod.o
  LD [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-rtdm.ko
  CC      /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-timer.mod.o
  LD [M]  /home/cpb/Documents/Articles/Blog/article-2012-05-14/oscillateur-gpio-timer.ko
make[1]: quittant le répertoire « /home/cpb/Projets/Panda/linux-2.6.38.8 »
$</pre>
<p style="text-align: justify;">Il faut ensuite transférer le code sur la cible, s&#8217;y connecter et charger le module.</p>
<pre>$ <strong>scp oscillateur-gpio-timer.ko root@192.168.5.152:/root</strong>
root@192.168.5.152's password:
oscillateur-gpio-timer.ko                                          100%   92KB  92.4KB/s   00:00
$ <strong>ssh root@192.168.5.152</strong>
root@192.168.5.152's password:
[Panda]# <strong>/sbin/insmod /root/oscillateur-gpio-timer.ko </strong>
[Panda]#</pre>
<p style="text-align: justify;">Sur l&#8217;oscilloscope, nous voyons bien un signal carré sur la broche 10 du connecteur d&#8217;extension de la Pandaboard.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-14.jpg"><img class="aligncenter size-full wp-image-2182" title="Oscillateur kernel avec API timer" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-14.jpg" alt="Oscillateur kernel avec API timer" width="600" height="444" /></a></p>
<p style="text-align: justify;">Pourtant ce signal est plutôt décevant. Nous avons demandé au noyau d&#8217;invoquer notre routine qui bascule l&#8217;état de la sortie toutes les millisecondes, et nous observons que les fronts successifs du signal sont espacés d&#8217;un peu moins de 4 carreaux, ce qui correspond à 8 millisecondes (réglage <em>Sweep</em> sur 2ms/division).</p>
<p style="text-align: justify;">Ceci s&#8217;explique par le fait que l&#8217;API <em>timer</em> du noyau s&#8217;appuie sur des ticks système. Or nous ne connaissons pas leur durée. Mais la constante symbolique <code>HZ</code> doit nous renseigner. Elle est configurée lors de la préparation de la compilation du noyau et se trouve donc dans fichier <code>.config</code> associé. Par habitude, j&#8217;active toujours les options &laquo;&nbsp;<em>Kernel .config support</em>&nbsp;&raquo; et &laquo;&nbsp;<em>Enable access to .config through /proc/config.gz</em>&nbsp;&raquo; (menu <em>General setup</em>) des noyaux que je compile afin d&#8217;embarquer une copie du fichier de configuration sur la cible.</p>
<pre>[Panda]# <strong>zcat config.gz | grep CONFIG_HZ</strong>
CONFIG_HZ=128
[Panda]#</pre>
<p style="text-align: justify;">Le tick du système est donc de 1/128 secondes, ce qui correspond à 7,81 millisecondes. Le noyau ne peut pas nous fournir de timer de résolution plus fine avec cette configuration.</p>
<p style="text-align: justify;">Retirons notre module du kernel et essayons d&#8217;améliorer le fonctionnement.</p>
<pre>[Panda]# <strong>/sbin/rmmod /root/oscillateur-gpio-timer.ko </strong>
[Panda]#</pre>
<h1>Les timers de haute précision du noyau Linux</h1>
<p style="text-align: justify;">Heureusement des timers de meilleure précision sont disponibles sous Linux. Ils ont été développés par Thomas Gleixner et Ingo Molnar dans le cadre de <em>Linux-rt</em> puis intégrés dans le noyau standard aux alentours de la version 2.6.26. Il s&#8217;agit de l&#8217;API <code>&lt;hrtimer.h&gt;</code> utilisable si l&#8217;option &laquo;&nbsp;<em>High Resolution Timer Support</em>&nbsp;&raquo; du menu &laquo;&nbsp;<em>Kernel Features</em>&nbsp;&raquo; a été activée lors de la compilation du kernel.</p>
<p style="text-align: justify;">Cette fois la définition des durées se fait en nanosecondes (ce qui ne signifie évidemment pas que le noyau soit capable de respecter la précision de la nanoseconde). En voici un petit exemple, où la période peut être précisée au chargement du module (en microsecondes pour conserver une meilleure lisibilité).</p>
<pre><a title="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-hrtimer.c" href="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-hrtimer.c" target="_blank"><strong>oscillateur-gpio-hrtimer.c :</strong></a>
#include &lt;linux/cdev.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/fs.h&gt;
#include &lt;linux/hrtimer.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/ktime.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/sched.h&gt;
#include &lt;linux/version.h&gt;
#include &lt;asm/uaccess.h&gt;

static enum hrtimer_restart timer_oscillateur(struct hrtimer *);
static <strong>struct hrtimer</strong> htimer;

static int periode_us = 1000;
module_param(periode_us, int, 0644);
static <strong>ktime_t</strong> kt_periode;

// 138 Broche 10 du port J3 (Expansion A) de la Pandaboard
#define GPIO_OSCILLATEUR 138

static int __init init_oscillateur (void)
{
  int err;

  kt_periode = <strong>ktime_set</strong>(0, 1000 * periode_us);

  if ((err = gpio_request(GPIO_OSCILLATEUR, THIS_MODULE-&gt;name)) != 0) {
    return err;
  }
  if ((err = gpio_direction_output(GPIO_OSCILLATEUR, 1)) != 0) {
    gpio_free(GPIO_OSCILLATEUR);
    return err;
  }
  <strong>hrtimer_init</strong> (&amp; htimer, CLOCK_REALTIME, HRTIMER_MODE_REL);
  <strong>htimer.function</strong> = timer_oscillateur;
  <strong>hrtimer_start</strong>(&amp; htimer, kt_periode, HRTIMER_MODE_REL);

  return 0;
}

static void __exit exit_oscillateur (void)
{
  <strong>hrtimer_cancel</strong>(&amp; htimer);
  gpio_free(GPIO_OSCILLATEUR);
}

static enum hrtimer_restart timer_oscillateur(struct hrtimer * unused)
{
  static int value = 0;
  gpio_set_value(GPIO_OSCILLATEUR, value);
  value = 1 - value;
  <strong>hrtimer_forward_now</strong>(&amp; htimer, kt_periode);
  return HRTIMER_RESTART;
}

module_init(init_oscillateur);
module_exit(exit_oscillateur);
MODULE_LICENSE("GPL");</pre>
<p style="text-align: justify;">Après compilation (comme précédemment) et transfert sur la cible, nous chargeons le module.</p>
<pre>[Panda]# <strong>/sbin/insmod /root/oscillateur-gpio-hrtimer.ko</strong>
[Panda]#</pre>
<p style="text-align: justify;">Nous voyons tout de suite la différence.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-15.jpg"><img class="aligncenter size-full wp-image-2186" title="Timer Kernel HrTimer 1000 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-15.jpg" alt="Timer Kernel HrTimer 1000 microsecondes" width="600" height="445" /></a></p>
<p style="text-align: justify;">Notre oscillateur semble bien basculer toutes les millisecondes, puisqu&#8217;il y a un cycle complet sur chaque division durant 2ms. Vérifions avec un calibre plus précis.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-16.jpg"><img class="aligncenter size-full wp-image-2187" title="Timer Kernel HrTimer 1000 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-16.jpg" alt="Timer Kernel HrTimer 1000 microsecondes" width="600" height="446" /></a></p>
<p style="text-align: justify;">Le <em>hrtimer</em> est bien déclenché toutes les millisecondes. Essayons de diminuer cette durée en commençant par une période de 100 microsecondes.</p>
<pre>[Panda]# <strong>/sbin/rmmod /root/oscillateur-gpio-hrtimer.ko</strong>
[Panda]# <strong>/sbin/insmod /root/oscillateur-gpio-hrtimer.ko periode_us=100</strong>
[Panda]#</pre>
<p style="text-align: justify;">Le résultat est concluant. Le calibre de l&#8217;oscilloscope est cette fois sur 20 microsecondes/division.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-17.jpg"><img class="aligncenter size-full wp-image-2188" title="Timer Kernel HrTimer 100 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-17.jpg" alt="Timer Kernel HrTimer 100 microsecondes" width="600" height="442" /></a></p>
<p style="text-align: justify;">Continuons à diminuer la période avec 10 microsecondes.</p>
<pre>[Panda]# <strong>/sbin/rmmod /root/oscillateur-gpio-hrtimer.ko</strong>
[Panda]# <strong>/sbin/insmod /root/oscillateur-gpio-hrtimer.ko periode_us=10</strong>
[Panda]#</pre>
<p style="text-align: justify;">L&#8217;oscillateur conserve sa précision. C&#8217;est remarquable.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-18.jpg"><img class="aligncenter size-full wp-image-2189" title="Timer Kernel HrTimer 10 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-18.jpg" alt="Timer Kernel HrTimer 10 microsecondes" width="600" height="455" /></a></p>
<p style="text-align: justify;">Toutefois lorsque la charge système augmente ou si de nombreuses interruptions surviennent, les fronts du signal se décalent sensiblement. Pour charger fortement en appels système notre carte, il y a une astuce simple : invoquer la commande :</p>
<pre>[Panda]# <strong>top -d 0</strong></pre>
<p style="text-align: justify;">Il y a alors des fluctuations assez importantes sur notre oscillateur. Voici une capture sur laquelle on voit que le niveau haut du signal dure près de onze microsecondes, et le niveau bas presque douze. Il y a d&#8217;autres fluctuations beaucoup plus grandes (mais difficiles à capturer !).</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-19.jpg"><img class="aligncenter size-full wp-image-2190" title="Timer Kernel HrTimer 10 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-19.jpg" alt="Timer Kernel HrTimer 10 microsecondes" width="600" height="435" /></a></p>
<p style="text-align: justify;">Cette sensibilité aux interruptions et à la quantité d&#8217;appels système est typique des systèmes temps-réel souples comme le noyau Linux classique.</p>
<h1>Un timer avec Xenomai et RTDM</h1>
<p style="text-align: justify;">Pour obtenir une meilleure tenue de notre signal, nous allons utiliser l&#8217;extension temps réel strict Xenomai. Pour l&#8217;installer sur la Pandaboard, on se reportera à <a title="Xenomai sur Pandaboard" href="http://www.blaess.fr/christophe/2011/11/27/xenomai-sur-pandaboard/">cet article</a>.</p>
<p style="text-align: justify;">Xenomai offre une API pour le développement de drivers temps réel nommée RTDM (<em>Real Time Driver Model</em>). Nous en avons déjà eu un aperçu dans <a title="Mesure de précision des timers de RTDM / Xenomai" href="http://www.blaess.fr/christophe/2012/05/07/mesure-de-precision-des-timers-de-rtdm-xenomai/">cet article</a>. Nous allons utiliser ce principe pour basculer notre broche GPIO.</p>
<pre><a title="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-rtdm.c" href="http://www.blaess.fr/christophe/files/article-2012-05-14/oscillateur-gpio-rtdm.c" target="_blank"><strong>oscillateur-gpio-rtdm.c :</strong></a>
#include &lt;linux/version.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/sched.h&gt;
#include &lt;linux/cdev.h&gt;
#include &lt;linux/gpio.h&gt;
#include &lt;linux/fs.h&gt;
#include &lt;asm/uaccess.h&gt;
#include &lt;rtdm/rtdm_driver.h&gt;

static int periode_us = 1000;
module_param(periode_us, int, 0644);

static void timer_oscillateur(rtdm_timer_t *);
static <strong>rtdm_timer_t</strong> rtimer;

// 138 Broche 10 du port J3 (Expansion A) de la Pandaboard
#define GPIO_OSCILLATEUR 138

static int __init init_oscillateur (void)
{
  int err;

  if ((err = <strong>gpio_request</strong>(GPIO_OSCILLATEUR, THIS_MODULE-&gt;name)) != 0) {
    return err;
  }
  if ((err = <strong>gpio_direction_output</strong>(GPIO_OSCILLATEUR, 1)) != 0) {
    gpio_free(GPIO_OSCILLATEUR);
    return err;
  }

  if ((err = <strong>rtdm_timer_init</strong>(&amp; rtimer, timer_oscillateur, "Oscillateur")) != 0) {
    gpio_free(GPIO_OSCILLATEUR);
    return err;
  }

  if ((err = <strong>rtdm_timer_start</strong>(&amp; rtimer, periode_us*1000, periode_us*1000, RTDM_TIMERMODE_RELATIVE)) != 0) {
    rtdm_timer_destroy(&amp; rtimer);
    gpio_free(GPIO_OSCILLATEUR);
    return err;
  }
  return 0;
}

static void __exit exit_oscillateur (void)
{
  <strong>rtdm_timer_stop</strong>(&amp; rtimer);
  <strong>rtdm_timer_destroy</strong>(&amp; rtimer);
 <strong> gpio_free</strong>(GPIO_OSCILLATEUR);
}

static void timer_oscillateur(rtdm_timer_t * unused)
{
  static int value = 0;
  gpio_set_value(GPIO_OSCILLATEUR, value);
  value = 1 - value;
}

module_init(init_oscillateur);
module_exit(exit_oscillateur);
MODULE_LICENSE("GPL");</pre>
<p style="text-align: justify;">Il n&#8217;y a rien de compliqué dans ce module. Nous le compilons avec le même <code>Makefile</code> que précédemment, mais il doit pouvoir trouver les fichiers d&#8217;en-tête de Xenomai, en interrogeant le script <code>xeno-config</code>. Chargeons notre module (après avoir déchargé le précédent), en lui demandant directement une période de 10 microsecondes.</p>
<pre>[Panda]# <strong>/sbin/rmmod /root/oscillateur-gpio-hrtimer.ko </strong>
[Panda]# <strong>/sbin/insmod /root/oscillateur-gpio-rtdm.ko periode_us=10</strong>
[Panda]#</pre>
<p>Nous observons que le signal est bien net et régulier, et ne fluctue pas avec la charge système.</p>
<p>&nbsp;</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-20.jpg"><img class="aligncenter size-full wp-image-2192" title="Timer Xenomai RTDM de 10 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-20.jpg" alt="Timer Xenomai RTDM de 10 microsecondes" width="600" height="442" /></a></p>
<p>Il est possible d&#8217;utiliser des périodes encore plus courtes. La capture ci-dessous a été obtenue avec un timer de période 2 microsecondes, alors qu&#8217;un <code><strong>top -d 0</strong></code> tournait sur la carte.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-21.jpg"><img class="aligncenter size-full wp-image-2193" title="Timer Xenomai RTDM à 2 microsecondes" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-21.jpg" alt="Timer Xenomai RTDM à 2 microsecondes" width="600" height="447" /></a></p>
<p style="text-align: justify;">Nous voyons que l&#8217;activité en appels système du noyau Linux (<code>top</code>) ne perturbe pas le timer implémenté par Xenomai.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">Nous avons examiné plusieurs méthodes pour réaliser des sorties sur une broche GPIO de la Pandaboard. Il nous reste à présent à nous intéresser aux entrées&#8230; Ce sera l&#8217;objet du prochain article.</p>
<p>Si vous souhaitez plus d&#8217;information sur les aspects temps réel abordés ici, je ne peux que vous conseiller mon nouveau livre &laquo;&nbsp;<a title="Solutions temps réel sous Linux" href="http://www.blaess.fr/christophe/livres/solutions-temps-reel-sous-linux/" target="_blank">Solutions temps réel sous Linux</a>&nbsp;&raquo; qui sera disponible en librairie (quel heureux hasard) à partir de demain !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/05/14/gpio-pandaboard-et-temps-reel-2-sorties-depuis-lespace-kernel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GPIO, Pandaboard et temps réel &#8211; 1 &#8211; Sorties depuis l&#8217;espace utilisateur</title>
		<link>http://www.blaess.fr/christophe/2012/05/09/gpio-pandaboard-temps-reel-1/</link>
		<comments>http://www.blaess.fr/christophe/2012/05/09/gpio-pandaboard-temps-reel-1/#comments</comments>
		<pubDate>Wed, 09 May 2012 09:50:02 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Embarqué]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Microprocesseur]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2120</guid>
		<description><![CDATA[Les GPIO (General Purpose Input Output) sont des broches du microprocesseur permettant de réaliser des opérations d&#8217;entrée-sortie électriques programmables. Chaque broche peut être affectée en entrée ou en sortie par programmation et utilisée aisément pour communiquer avec des périphériques externes. La Pandaboard utilise un processeur Texas OMAP4430, construit autour d&#8217;un coeur Cortex A9. La documentation [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-0.jpg"><img class="aligncenter size-full wp-image-2164" title="GPIO Pandaboard et temps-réel" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-0.jpg" alt="GPIO Pandaboard et temps-réel" width="500" height="670" /></a></p>
<p style="text-align: justify;">Les <strong>GPIO</strong> (<em>General Purpose Input Output</em>) sont des broches du microprocesseur permettant de réaliser des opérations d&#8217;entrée-sortie électriques programmables. Chaque broche peut être affectée en entrée ou en sortie par programmation et utilisée aisément pour communiquer avec des périphériques externes.</p>
<p style="text-align: justify;">La Pandaboard utilise un processeur Texas OMAP4430, construit autour d&#8217;un coeur Cortex A9. La <a title="http://www.ti.com/pdfs/wtbu/OMAP4430_ES2.x_PUBLIC_TRM_vAE.zip" href="http://www.ti.com/pdfs/wtbu/OMAP4430_ES2.x_PUBLIC_TRM_vAE.zip" target="_blank">documentation techique</a> de l&#8217;OMAP4430 (plus de 5500 pages&#8230;) précise que le processeur dispose de six modules GPIO, chacun offrant 32 lignes d&#8217;entrées-sorties. Sur les 192 ports GPIO théoriquement présents, une bonne partie est déjà employée pour la carte elle-même mais quelques uns sont accessibles à l&#8217;utilisateur.</p>
<h1>Connecteurs d&#8217;extension</h1>
<p style="text-align: justify;">Nous trouvons sur la Pandaboard deux connecteurs utlisables pour des expérimentations et extensions futures. Numérotés J3 et J6, ils sont également repérés par les libellés &laquo;&nbsp;<em>Expansion Connector A</em>&nbsp;&raquo; et &laquo;&nbsp;<em>Expansion Connector B</em>&nbsp;&raquo; comme on peut le voir sur la photo ci-contre.<a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-1.jpg"><img class="alignright size-full wp-image-2124" title="Connecteurs d'extension de la Pandaboard." src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-1.jpg" alt="Connecteurs d'extension de la Pandaboard." width="320" height="216" /></a></p>
<p style="text-align: justify;">Le détail des broches disponibles est fourni dans <a title="http://www.blaess.fr/christophe/files/article-2012-05-10/Panda_Board_Spec_REVEA1_04.pdf" href="http://www.blaess.fr/christophe/files/article-2012-05-10/Panda_Board_Spec_REVEA1_04.pdf" target="_blank">le manuel de référence de la Pandaboard</a> (pages 43 et 44), certaines d&#8217;entre elles ayant une signification pour le système.</p>
<p>&nbsp;</p>
<p style="text-align: justify;">Par exemple, dans le tableau ci-dessous nous pouvons remarquer que la broche numéro 10 est associée à deux fonctionnalités : un signal <em>Chip Select</em> pour une communication avec une autre carte ou une entrée sortie GPIO numéro 138.</p>
<p style="text-align: center;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-2.jpg"><img class="aligncenter size-full wp-image-2127" title="GPIO du connecteur A" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-2.jpg" alt="GPIO du connecteur A" width="685" height="539" /></a></p>
<p style="text-align: justify;">Certaines broches ont des affectations figées car elles sont reliées à des composants sur la carte, mais d&#8217;autres peuvent être utilisées pour réaliser des entrées sorties personnalisées.</p>
<p style="text-align: justify;">Nous remarquons la présence sur la broche numéro 1 d&#8217;un signal d&#8217;alimentation +1.8 V que nous pourrons utiliser pour l&#8217;envoyer sur les entrées de notre choix (dans le prochain article) et de la masse du signal sur les broches 27 et 28.</p>
<p style="padding-left: 60px; text-align: justify;">Attention, les opérations sur ce connecteur sont très risquées car elles se répercutent directement sur les broches du processeur, sans protection. Je décline donc toute responsabilité si une manipualtion décrite ici sonne le glas de votre Pandaboard. Pour vous convaincre encore, sachez que j&#8217;ai grillé une carte Pandaboard (enfin, un processeur OMAP4 mais c&#8217;est le principal) simplement en confondant la broche 2 (alimentation +5V) et la broche 1 (alimentation +1.8V) pour envoyer un signal sur une entrée GPIO. Donc : prudence et concentration sont de rigueur !</p>
<p style="text-align: justify;"> Nous allons donc souder sur le connecteur quelques fils afin d&#8217;accéder facilement aux broches 1, 10 et 28.</p>
<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-3.jpg"><img class="aligncenter size-full wp-image-2137" title="Broches soudées sur connecteur d'extension" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-3.jpg" alt="Broches soudées sur connecteur d'extension" width="400" height="197" /></a>La broche 1 de chaque connecteur (en haut à gauche lorsqu&#8217;on le regarde de face) est repérée par un carré, la 2 se trouve en dessous, la 3 à droite du 1, la 4 en dessous de la 3 et ainsi de suite.</p>
<p style="text-align: justify;">Naturellement, la soudure se fait plutôt sur la face opposée, où les numérotations sont inversées.</p>
<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-4.jpg"><img class="aligncenter size-full wp-image-2138" title="Soudure au verso du connecteur d'extension" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-4.jpg" alt="Soudure au verso du connecteur d'extension" width="400" height="250" /></a>Pour cet article, nous n&#8217;utiliserons pas l&#8217;alimentation (broche 1), mais j&#8217;ai profité de l&#8217;occasion pour la souder en même temps les deux autres.</p>
<h1>Accès aux GPIO depuis l&#8217;espace utilisateur avec le shell</h1>
<p style="text-align: justify;">Démarrons notre carte en utilisant un système fait maison, comme décrit dans les articles <a title="Construire son système personnel sur une carte Pandaboard (1)" href="http://www.blaess.fr/christophe/2011/05/06/construire-son-systeme-personnel-sur-une-carte-pandaboard/">numéro 1</a>, <a title="Construire son système personnel sur une carte Pandaboard (2)" href="http://www.blaess.fr/christophe/2011/05/13/construire-son-systeme-personnel-sur-une-carte-pandaboard-2/">numéro 2</a>, <a title="Construire son système personnel sur une carte Pandaboard (3)" href="http://www.blaess.fr/christophe/2011/05/20/construire-son-systeme-personnel-sur-une-carte-pandaboard-3/">numéro 3</a>, <a title="Construire son système personnel sur une carte Pandaboard (4)" href="http://www.blaess.fr/christophe/2011/06/10/construire-son-systeme-personnel-sur-une-carte-pandaboard-4/">numéro 4</a> et <a title="Construire son système personnel sur carte Pandaboard (5)" href="http://www.blaess.fr/christophe/2011/06/17/construire-son-systeme-personnel-sur-carte-pandaboard-5/">numéro 5</a> d&#8217;une précédente série. Puis connectons depuis un PC distant en utilisant le protocole SSH.</p>
<pre>[~]$ <strong>ssh root@192.168.5.152</strong>
root@192.168.5.152's password:
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">Le <em>prompt</em> du shell sur la Pandaboard est préfixé par &laquo;&nbsp;[Panda]&nbsp;&raquo; pour éviter les confusions avec le système hôte.</p>
<p style="text-align: justify;">Vérifions pour commencer la tension sur la broche 10 avec un simple voltmètre.</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-5.jpg"><img class="aligncenter size-full wp-image-2140" title="GPIO en sortie à 0V" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-5.jpg" alt="GPIO en sortie à 0V" width="600" height="413" /></a></p>
<p style="text-align: justify;">La tension est nulle, mais la broche est par défaut affectée en entrée (avec une haute impédance). Nous allons modifier cela.</p>
<pre>[Panda]# <strong>cd /sys/class/gpio/</strong>
[Panda]# <strong>ls</strong>
export       gpio62       gpiochip128  gpiochip32   gpiochip96
gpio1        gpiochip0    gpiochip160  gpiochip64   unexport
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">Par défaut, le port GPIO 138 n&#8217;est pas accessible depuis le pseudo système de fichier <code>/sys/</code>, mais nous pouvons le réclamer ainsi.</p>
<pre>[Panda]# <strong>echo 138 &gt; export </strong>
[Panda]# <strong>ls</strong>
export       <strong>gpio138</strong>      gpiochip0    gpiochip160  gpiochip64   unexport
gpio1        gpio62       gpiochip128  gpiochip32   gpiochip96
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">Le port 138 est maintenant accessible vérifions son sens de fonctionnement.</p>
<pre>[Panda]# <strong>cd gpio138/</strong>
[Panda]# <strong>cat direction</strong>
in
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">Il est affecté en entrée par sécurité au <em>boot</em>. Basculons-le en sortie.</p>
<pre>[Panda]# <strong>echo out &gt; direction </strong>
[Panda]# <strong>cat direction </strong>
out
[Panda]# <strong>cat value</strong>
0
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">La valeur inscrite sur le port est <code>0</code>, ce qui ne nous surprend pas puisque la tension sur la borne est nulle. Modifions la valeur.</p>
<pre>[Panda]# <strong>echo 1 &gt; value </strong>
[Panda]#</pre>
<p style="padding-left: 30px; text-align: justify;">Le voltmètre affiche maintenant 1,8 V (la tension de référence pour les GPIO, à ne pas dépasser pour les entrées !).</p>
<p><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-6.jpg"><img class="aligncenter size-full wp-image-2141" title="GPIO en sortie à 1,8 V." src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-6.jpg" alt="GPIO en sortie à 1,8 V." width="600" height="514" /></a></p>
<p style="text-align: justify;">Écrivons à nouveau sur le port.</p>
<pre>[Panda]# <strong>echo 0 &gt; value </strong>
[Panda]#</pre>
<p style="text-align: justify;">Le voltmètre affiche à nouveau 0.00. Libérons le port GPIO pour terminer cette expérience.</p>
<pre>[Panda]# <strong>cd /sys/class/gpio/</strong>
[Panda]# <strong>echo 138 &gt; unexport </strong>
[Panda]#</pre>
<h1>Application des GPIO avec un script shell</h1>
<p style="text-align: justify;">Nous savons écrire une valeur sur une broche de sortie depuis la ligne de commande, automatisons cela dans un petit script shell qui va faire osciller la broche 10.</p>
<pre><a title="http://www.blaess.fr/christophe/files/article-2012-05-10/oscillateur-gpio.sh" href="http://www.blaess.fr/christophe/files/article-2012-05-10/oscillateur-gpio.sh" target="_blank"><strong>/usr/local/bin/oscillateur-gpio.sh :</strong></a>
#! /bin/sh

GPIO=138  <em># Broche 10 du port "Expansion A"</em>

cd /sys/class/gpio

echo ${GPIO} &gt; export

cd gpio${GPIO}

echo out &gt; direction

while true
do
	echo 1 &gt; value
	usleep 1000
	echo 0 &gt; value
	usleep 1000
done</pre>
<p style="text-align: justify;">Le script réalise de petites pause de 1000 microsecondes, soit une milliseconde après chaque changement d&#8217;état de la broche.</p>
<p style="text-align: justify;">Connectons un oscilloscope à la place du voltmètre pour voir évoluer le signal.</p>
<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-7.jpg"><img class="aligncenter size-full wp-image-2144" title="Oscilloscope sur port de sortie GPIO" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-7.jpg" alt="Oscilloscope sur port de sortie GPIO" width="600" height="439" /></a>Nous apercevons sur l&#8217;écran un signal périodique qui alterne entre 0 et 1,8V. Toutefois, en regardant de plus près, les durées que nous avions indiquées dans le script ne sont pas respectées.</p>
<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-8.jpg"><img class="aligncenter size-full wp-image-2145" title="Oscillateur GPIO par script shell" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-8.jpg" alt="Oscillateur GPIO par script shell" width="600" height="443" /></a>Le signal change de niveau toutes les 3,3 millisecondes environ et non pas à chaque milliseconde. Il s&#8217;agit de la granularité du <code>usleep</code> qui pose problème.Nous pouvons améliorer ceci en utilisant un petit programme en C qui réalise le même travail mais s&#8217;appuie directement sur la libC en passant outre les latences dûes au shell et aux utilitaires système (issus de la Busybox en l&#8217;occurrence).</p>
<pre><a title="http://www.blaess.fr/christophe/files/article-2012-05-10/oscillateur-gpio-user.c" href="http://www.blaess.fr/christophe/files/article-2012-05-10/oscillateur-gpio-user.c" target="_blank"><strong>oscillateur-gpio-user.c :</strong></a>
#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;

static volatile int exit_loop = 0;

void handler_signal(int unused)
{
    exit_loop = 1;
}

int main(int argc, char * argv[])
{
    int value = 1;
    char path[256];
    int gpio = 138;
    FILE * fp;

    // En argument on peut preciser un GPIO different de 138
    if ((argc &gt; 2) || ((argc == 2) &amp;&amp; (sscanf(argv[1], "%d", &amp; gpio) != 1))) {
        fprintf(stderr, "usage: %s [gpio]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // Exporter le GPIO dans le systeme de fichiers
    snprintf(path, 256, "/sys/class/gpio/export");
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "%d\n", gpio);
    fclose(fp);

    // Intercepter Controle-C pour finir proprement
    signal(SIGINT, handler_signal);

    // Basculer le GPIO en sortie
    snprintf(path, 256, "/sys/class/gpio/gpio%d/direction", gpio);
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "out\n");
    fclose(fp);

    // Ecrire alternativement la valeur du GPIO
    snprintf(path, 256, "/sys/class/gpio/gpio%d/value", gpio);
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    while (! exit_loop) {
        fprintf(fp, "%d\n", value);
        fflush(fp);
        value = 1 - value;
        usleep(1000);
    }

    // De-exporter le GPIO du systeme de fichiers
    if ((fp = fopen("/sys/class/gpio/unexport", "w")) == NULL) {
        perror("/sys/class/gpio/unexport");
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "%d\n", gpio);
    fclose(fp);
    return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Après compilation (en utilisant un cross-compiler comme indiqué dans <a title="Buildroot vs Crosstool-NG – Compilons notre cross-compiler" href="http://www.blaess.fr/christophe/2011/05/27/buildroot-vs-crosstool-ng-compilons-notre-cross-compiler/" target="_blank">cet article</a>), nous transférons le code sur la cible.</p>
<pre>$ <strong>~/cross-panda/usr/bin/arm-linux-gcc -Wall -o oscillateur-gpio-user oscillateur-gpio-user.c</strong>
$ <strong>scp oscillateur-gpio-user root@192.168.5.152:/root/</strong>
root@192.168.5.152's password:
oscillateur-gpio-user                                               100% 6270     6.1KB/s   00:00
$</pre>
<p style="text-align: justify;">Sur la cible nous lançons le programme (et nous pouvons l&#8217;arrêter avec <em>Contrôle-C</em>).</p>
<pre>[Panda]# <strong>/root/oscillateur-gpio-user </strong>
    <em>(<strong>Contrôle-C</strong>)</em>
[Panda]#</pre>
<p style="text-align: justify;">Le signal est plus précis, nous pourrions calibrer la durée des sommeils pour avoir exactement une milliseconde, ou utiliser un timer logiciel comme ceux founis par <code>setitimer()</code>.</p>
<p style="text-align: center;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-11.jpg"><img class="aligncenter size-full wp-image-2151" title="Oscillateur GPIO par programme C - Temps partagé" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-11.jpg" alt="Oscillateur GPIO par programme C - Temps partagé" width="600" height="449" /></a></p>
<p style="text-align: justify;">Toutefois, sur l&#8217;écran de l&#8217;oscilloscope, le signal n&#8217;est pas très stable, ses fronts montants et descendants tremblent en permanence, signe classique d&#8217;un comportement lié à l&#8217;ordonnancement en temps partagé.</p>
<p style="text-align: justify;">Pour améliorer la précision nous pouvons lancer le programme en ordonnancement temps réel souple avec <code>chrt</code>.</p>
<pre>[Panda]# <strong>chrt -f99 /root/oscillateur-gpio-user</strong>
    <em>(<strong>Contrôle-C</strong>)</em>
[Panda]#</pre>
<p style="text-align: center;"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-12.jpg"><img class="aligncenter size-full wp-image-2153" title="Oscillateur GPIO par programme C - Temps réel souple" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/img-12.jpg" alt="Oscillateur GPIO par programme C - Temps réel souple" width="600" height="448" /></a></p>
<p style="text-align: justify;">Le signal est beaucoup plus stable, mais il y a encore des petits &laquo;&nbsp;sauts&nbsp;&raquo; de temps à autres. Avec un oscilloscope à mémoire, nous pourrions voir apparaître parfois des fronts décalés d&#8217;une durée assez significative.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">Afin d&#8217;améliorer la précision d&#8217;un signal, il est nécessaire d&#8217;adopter une approche temps réel. Ceci peut se réaliser de différentes manières. Dans <a title="GPIO, Pandaboard et temps réel – 2 – sorties depuis l’espace kernel" href="http://www.blaess.fr/christophe/2012/05/14/gpio-pandaboard-et-temps-reel-2-sorties-depuis-lespace-kernel/">le prochain article</a> nous verrons comment gérer les GPIO depuis l&#8217;espace kernel en écrivant un module pour le noyau Linux standard, puis nous nous rapprocherons du temps réel strict en pilotant les GPIO depuis un module kernel de Xenomai, avec l&#8217;API RTDM.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/05/09/gpio-pandaboard-temps-reel-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mesure de précision des timers de RTDM / Xenomai</title>
		<link>http://www.blaess.fr/christophe/2012/05/07/mesure-de-precision-des-timers-de-rtdm-xenomai/</link>
		<comments>http://www.blaess.fr/christophe/2012/05/07/mesure-de-precision-des-timers-de-rtdm-xenomai/#comments</comments>
		<pubDate>Mon, 07 May 2012 08:00:53 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2093</guid>
		<description><![CDATA[Cette petite expérience va nous permettre de mesurer la précision des timers kernel programmés en utilisant l&#8217;API RTDM (Real Time Driver Model) proposée par Xenomai dans sa version 2.6.0. Il est important de noter que RTDM est une spécification pour le développement de drivers temps-réel pour Linux qui pourra prochainement être utilisée directement avec le [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Cette petite expérience va nous permettre de mesurer la précision des timers kernel programmés en utilisant l&#8217;API <strong>RTDM</strong> (<em>Real Time Driver Model</em>) proposée par Xenomai dans sa version 2.6.0. Il est important de noter que RTDM est une spécification pour le développement de drivers temps-réel pour Linux qui pourra prochainement être utilisée directement avec le noyau &laquo;&nbsp;patché&nbsp;&raquo; <em>Linux-rt</em>. Il est donc utile de commencer à s&#8217;intéresser à cette API pour tout les développements kernel ayant spécifiquement trait au temps réel.</p>
<h1>Module dans l&#8217;espace noyau</h1>
<p style="text-align: justify;">Notre module va installer un timer avec une période d&#8217;une milliseconde. A chaque déclenchement, le timer mesurera l&#8217;écart par rapport au déclenchement précédent et proposera ses résultats dans une interface RTDM accessible depuis l&#8217;espace utilisateur.</p>
<p style="text-align: justify;">Voici un exemple d&#8217;implémentation, les fichiers sources se trouvent dans <a title="http://www.blaess.fr/christophe/files/article-2012-05-07/mesure-periode-rtdm.tar.bz2" href="http://www.blaess.fr/christophe/files/article-2012-05-07/mesure-periode-rtdm.tar.bz2">cette archive</a>.</p>
<pre><strong>mesure-periode-rtdm.c: </strong>
#include &lt;linux/version.h&gt;
#include &lt;linux/device.h&gt;
#include &lt;linux/module.h&gt;
#include &lt;linux/sched.h&gt;
#include &lt;linux/cdev.h&gt;
#include &lt;linux/fs.h&gt;

#include &lt;asm/uaccess.h&gt;

#include &lt;rtdm/rtdm_driver.h&gt;

#define nom_device_rtdm "mesure_periode_rtdm"

static int read_mesure_periode(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, void * buffer, size_t lg);
static int open_nonrt_mesure_periode (struct rtdm_dev_context * contexte, rtdm_user_info_t * info, int flags);
static int close_nonrt_mesure_periode (struct rtdm_dev_context * contexte, rtdm_user_info_t * info);
static void handler_mesure_periode (rtdm_timer_t * timer);

static <strong>struct rtdm_device</strong> rtdev_mesure_periode = {
    .struct_version = RTDM_DEVICE_STRUCT_VER,
    .device_flags   = RTDM_NAMED_DEVICE,
    .context_size   = 0,
    .device_name    = nom_device_rtdm,
    .open_nrt       = open_nonrt_mesure_periode,
    .ops = {
        .close_nrt = close_nonrt_mesure_periode,
        .read_nrt  = read_mesure_periode,
    },
    .device_class     = RTDM_CLASS_TESTING,
    .device_sub_class = 1,
    .profile_version  = 1,
    .driver_name      = nom_device_rtdm,
    .driver_version   = RTDM_DRIVER_VER(1,0,0),
    .peripheral_name  = nom_device_rtdm,
    .provider_name    = "cpb",
    .proc_name        = nom_device_rtdm,
};

static nanosecs_abs_t  periode_mini;
static nanosecs_abs_t  periode_maxi;
static nanosecs_abs_t  somme_periodes;
static int             nb_periodes;
static rtdm_mutex_t mtx_statistiques;

static rtdm_timer_t rtimer;
static nanosecs_rel_t periode_desiree = 1000000; // 1 ms

static int __init <strong>init_mesure_periode</strong> (void)
{
    int err;

    <strong>rtdm_mutex_init</strong>(&amp; mtx_statistiques);

    periode_mini = -1;
    periode_maxi = 0;
    somme_periodes = 0;
    nb_periodes = 0;

    <strong>rtdm_dev_register</strong>(&amp; rtdev_mesure_periode);
    if ((err = <strong>rtdm_timer_init</strong>(&amp; rtimer, handler_mesure_periode, "Timer_mesure_periode")) != 0) {
        rtdm_dev_unregister(&amp; rtdev_mesure_periode, 0);
        rtdm_mutex_destroy(&amp; mtx_statistiques);
        return err;
    }

    if ((err = <strong>rtdm_timer_start</strong>(&amp; rtimer, 1000000000, periode_desiree, RTDM_TIMERMODE_RELATIVE)) != 0) {
        rtdm_timer_destroy(&amp; rtimer);
        rtdm_dev_unregister(&amp; rtdev_mesure_periode, 0);
        rtdm_mutex_destroy(&amp; mtx_statistiques);
        return err;
    }

    return 0;
}

static void __exit <strong>exit_mesure_periode</strong>(void)
{
    <strong>rtdm_timer_stop</strong>(&amp; rtimer);
    <strong>rtdm_timer_destroy</strong>(&amp; rtimer);
    <strong>rtdm_dev_unregister</strong>(&amp; rtdev_mesure_periode, 0);
    <strong>rtdm_mutex_destroy</strong>(&amp; mtx_statistiques);
}

static int <strong>read_mesure_periode</strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, void * buffer, size_t lg)
{
    char local_buffer[80];
    nanosecs_abs_t  _periode_mini;
    nanosecs_abs_t  _periode_maxi;
    nanosecs_abs_t  _somme_periodes;
    int             _nb_periodes;

    <strong>rtdm_mutex_lock</strong>(&amp; mtx_statistiques);
    _periode_mini   = periode_mini;
    _periode_maxi   = periode_maxi;
    _somme_periodes = somme_periodes;
    _nb_periodes    = nb_periodes;
    <strong>rtdm_mutex_unlock</strong>(&amp; mtx_statistiques);

    snprintf(local_buffer, 80, "%lld %lld %lld %d\n",
        _periode_mini, _periode_maxi, _somme_periodes, _nb_periodes);
    if (<strong>rtdm_safe_copy_to_user</strong>(info, buffer, local_buffer, strlen(local_buffer)) != 0)
        return -EFAULT;
    return strlen(local_buffer);
}

static int <strong>open_nonrt_mesure_periode</strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, int flags)
{
    return 0;
}

static int <strong>close_nonrt_mesure_periode</strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info)
{
    return 0;
}

static void <strong>handler_mesure_periode</strong> (rtdm_timer_t * timer)
{
    nanosecs_abs_t         heure;
    static nanosecs_abs_t  precedente;
    nanosecs_abs_t         periode;

    heure = <strong>rtdm_clock_read</strong>();
    <strong>rtdm_mutex_lock</strong>(&amp; mtx_statistiques);
    if (nb_periodes &gt; 0) {
        periode = heure - precedente;
        if (periode &gt; periode_maxi)
            periode_maxi = periode;
        if ((periode_mini == -1) || (periode_mini &gt; periode))
            periode_mini = periode;
        somme_periodes += periode;
    }
    nb_periodes ++;
    <strong>rtdm_mutex_unlock</strong>(&amp; mtx_statistiques);
    precedente = heure;
}

<strong>module_init</strong>(init_mesure_periode);
<strong>module_exit</strong>(exit_mesure_periode);
MODULE_LICENSE("GPL");</pre>
<p style="text-align: justify;">Compilons ce module, chargeons-le et vérifions qu&#8217;il soit bien enregistré. Naturellement, il est nécessaire d&#8217;avoir installé un noyau Linux/Xenomai comme décrit dans <a title="Xenomai 2.6.0 sur Ubuntu 12.04" href="http://www.blaess.fr/christophe/2012/05/02/xenomai-2-6-0-sur-ubuntu-12-04/" target="_blank">cet article</a> ou <a title="Xenomai sur Pandaboard" href="http://www.blaess.fr/christophe/2011/11/27/xenomai-sur-pandaboard/" target="_blank">celui-ci</a>.</p>
<pre># <strong>make</strong>
make -C /lib/modules/2.6.38.8-xenomai/build SUBDIRS=/home/cpb/Documents/Livres/Articles/Blog/article-2012-05-07  modules
make[1]: entrant dans le répertoire « /usr/local/src/linux-2.6.38.8-xenomai »
  CC [M]  /home/cpb/Documents/Livres/Articles/Blog/article-2012-05-07/mesure-periode-rtdm.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/cpb/Documents/Livres/Articles/Blog/article-2012-05-07/mesure-periode-rtdm.mod.o
  LD [M]  /home/cpb/Documents/Livres/Articles/Blog/article-2012-05-07/mesure-periode-rtdm.ko
make[1]: quittant le répertoire « /usr/local/src/linux-2.6.38.8-xenomai »
gcc -I/usr/xenomai/include -D_GNU_SOURCE -D_REENTRANT -Wall -Werror-implicit-function-declaration -pipe -D__XENO__ -lrtdm -L/usr/xenomai/lib -lxenomai -lpthread -lrt -L /usr/xenomai/lib -o mesure-periode mesure-periode.c -lrtdm -lxenomai
# <strong>insmod ./mesure-periode-rtdm.ko </strong>
# <strong>cat /proc/xenomai/rtdm/named_devices</strong>
Hash	Name				Driver		/proc
24	rttest-timerbench0             	xeno_timerbench	rttest-timerbench0
55	rttest-switchtest0             	xeno_switchtest	rttest-switchtest0
EE	mesure_periode_rtdm            	mesure_periode_rtdm	mesure_periode_rtdm
#</pre>
<h1>Application dans l&#8217;espace utilisateur</h1>
<p style="text-align: justify;">Contrairement aux drivers Linux habituels, RTDM ne fournit pas de fichier spécial (avec un type, un numéro majeur et un numéro mineur) pour accéder aux fonctionnalités implémentés. Le driver n&#8217;est pas accessible par un appel <code>open()</code> sur un fichier de <code>/dev</code> mais par une opération spécifique <code>rt_dev_open()</code> employant le nom du périphérique.</p>
<p style="text-align: justify;">Voici un petit utilitaire qui ouvre le périphérique <code>mesure_periode_rtdm</code> et lit les valeurs. Il affiche sur sa sortie standard les informations obtenues.</p>
<pre><strong>mesure-periode.c: </strong>
#include &lt;fcntl.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;

#include &lt;rtdm/rtdm.h&gt;

#define LG_BUFFER 80

int main (int argc, char * argv[])
{
    int fd;
    int i;
    char buffer[LG_BUFFER];
    long long int min, max, somme;
    int nb;
    fd = <strong>rt_dev_open</strong>("mesure_periode_rtdm", O_RDONLY);
    if (fd &lt; 0) {
        fprintf(stderr, "%s: %s\n", argv[1], strerror(-fd));
        exit(EXIT_FAILURE);
    }

    while ((i = <strong>rt_dev_read</strong>(fd, buffer, LG_BUFFER)) &gt; 0) {
        buffer[i] ='\0';
        if (sscanf(buffer, "%lld %lld %lld %d", &amp;min, &amp;max, &amp;somme, &amp;nb) == 4)
            printf("min = %lld, max = %lld, moy = %lld\n", min, max, somme / nb);
    }
    <strong>rt_dev_close</strong>(fd);
    return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Nous pouvons lancer le programme, il affichera sur sa sortie standard les valeurs statistiques obtenues.</p>
<pre># <strong>export LD_LIBRARY_PATH=/usr/xenomai/lib/</strong>
# <strong>./mesure-periode</strong>
[...]
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
min = 968234, max = 1036666, moy = 999995
[...]
#</pre>
<h1>Résultats</h1>
<p style="text-align: justify;">Sur ce poste, nous voyons que le timer à 1kHz a subi des fluctuations maximales de 37 microsecondes environ, après un quart d&#8217;heure de fonctionnement. La machine n&#8217;était pas excessivement chargée, ni en interruptions ni en processus. Avec une charge beaucoup plus élevée, on pourrait probablement s&#8217;attendre à des fluctuations approchant la centaine de microsecondes.</p>
<p style="text-align: justify;">Cette exécution s&#8217;est faite sur un poste de bureautique d&#8217;entrée de gamme, qui n&#8217;est donc pas particulièrement conçu pour le temps réel (notamment dans la gestion des interruptions SMI). Avec un PC industriel et un noyau bien configuré, j&#8217;ai déjà observé des fluctuations inférieures à 35 microsecondes sur deux jours alors que la machine était très fortement chargée, tant en activité CPU (processus, appel-système) qu&#8217;en interruptions diverses.</p>
<p style="text-align: justify;">Si vous obtenez des résultats intéressants avec cette expérience, communiquez-les moi et je serai heureux de les faire figurer ici.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/05/07/mesure-de-precision-des-timers-de-rtdm-xenomai/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Xenomai 2.6.0 sur Ubuntu 12.04</title>
		<link>http://www.blaess.fr/christophe/2012/05/02/xenomai-2-6-0-sur-ubuntu-12-04/</link>
		<comments>http://www.blaess.fr/christophe/2012/05/02/xenomai-2-6-0-sur-ubuntu-12-04/#comments</comments>
		<pubDate>Wed, 02 May 2012 06:00:28 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2068</guid>
		<description><![CDATA[J&#8217;ai vérifié hier que Xenomai 2.6.0 (la dernière version stable à ce jour) s&#8217;installe bien sur la nouvelle distribution Ubuntu. Pas de souci, il suffit de procéder comme pour la distribution précédente (voir cet article) en utilisant ce fichier de configuration pour le noyau Linux 2.6.38.8.]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;"><a href="http://www.blaess.fr/christophe/2012/05/02/xenomai-2-6-0-sur-ubuntu-12-04/ubuntu-xenomai/" rel="attachment wp-att-2071"><img class="alignleft size-full wp-image-2071" title="Ubuntu + Xenomai" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/05/ubuntu-xenomai.jpg" alt="Xenomai 2.6.0 sur Ubuntu 12.04" width="200" height="203" /></a>J&#8217;ai vérifié hier que <a title="Xenomai 2.6.0" href="http://www.xenomai.org/index.php/Xenomai:News#2011-11-05_Xenomai_2.6.0" target="_blank">Xenomai 2.6.0</a> (la dernière version stable à ce jour) s&#8217;installe bien sur la <a title="Ubuntu 12.04" href="http://doc.ubuntu-fr.org/precise" target="_blank">nouvelle distribution Ubuntu</a>. Pas de souci, il suffit de procéder comme pour la distribution précédente (voir <a title="Xenomai 2.6.0 sur Ubuntu 11.10" href="http://www.blaess.fr/christophe/2011/12/28/xenomai-2-6-0-sur-ubuntu-11-10/" target="_blank">cet article</a>) en utilisant <a title="http://www.blaess.fr/christophe/files/article-2012-05-02/config-linux-2.6.38.8-xenomai" href="http://www.blaess.fr/christophe/files/article-2012-05-02/config-linux-2.6.38.8-xenomai" target="_blank">ce fichier de configuration</a> pour le noyau <a title="Linux 2.6.38.8" href="http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.38.8.tar.bz2" target="_blank">Linux 2.6.38.8</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/05/02/xenomai-2-6-0-sur-ubuntu-12-04/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Modifier facilement la fréquence processeur</title>
		<link>http://www.blaess.fr/christophe/2012/04/30/modifier-facilement-la-frequence-processeur/</link>
		<comments>http://www.blaess.fr/christophe/2012/04/30/modifier-facilement-la-frequence-processeur/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 08:00:47 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Embarqué]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Microprocesseur]]></category>
		<category><![CDATA[Temps-réel]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2041</guid>
		<description><![CDATA[Linux offre une possibilité intéressante : celle de configurer assez finement la vitesse du processeur depuis la ligne de commande. Il permet d&#8217;agir indépendamment (pour autant que l&#8217;architecture matérielle le permette) sur les différents coeurs des processeurs. On m&#8217;a demandé récemment une petite illustration pratique de ces possibilités : en voici un résumé. Configurer la [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Linux offre une possibilité intéressante : celle de configurer assez finement la vitesse du processeur depuis la ligne de commande. Il permet d&#8217;agir indépendamment (pour autant que l&#8217;architecture matérielle le permette) sur les différents coeurs des processeurs. On m&#8217;a demandé récemment une petite illustration pratique de ces possibilités : en voici un résumé.</p>
<h1>Configurer la vitesse CPU avec <code>/sys</code></h1>
<p style="text-align: justify;">La première opération nécessaire est de connaître la liste des fréquences supportées par un CPU. Cette liste est accessible grâce au pseudo système de fichier <code>/sys</code>.</p>
<pre># <strong>ls /sys/devices/system/cpu/</strong>
cpu0  cpu1  cpu2  cpu3  cpufreq  cpuidle  kernel_max  offline  online  possible  present  probe  release  sched_mc_power_savings
# <strong>cat /sys/devices/system/cpu/online </strong>
0-3
# <strong>cat /sys/devices/system/cpu/possible </strong>
0-7
# <strong>cat /sys/devices/system/cpu/present </strong>
0-3
#</pre>
<p style="text-align: justify;">Ici, le noyau peut gérer sept CPU (paramètre <code>possible</code>), mais il n&#8217;y en a que quatre connectés (<code>present</code>). Ils sont tous actifs (<code>online</code>). Choisissons, arbitrairement, le CPU numéro 1 et vérifions ses fréquences de fonctionnement.</p>
<pre># <strong>ls /sys/devices/system/cpu/cpu1/</strong>
cache  cpufreq  crash_notes  online  thermal_throttle  topology
# <strong>ls /sys/devices/system/cpu/cpu1/cpufreq/</strong>
affected_cpus               related_cpus                   scaling_max_freq
bios_limit                  scaling_available_frequencies  scaling_min_freq
cpuinfo_cur_freq            scaling_available_governors    scaling_setspeed
cpuinfo_max_freq            scaling_cur_freq               stats
cpuinfo_min_freq            scaling_driver
cpuinfo_transition_latency  scaling_governor
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies </strong>
2400000 2133000 1867000 1600000
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq </strong>
2400000
#</pre>
<p style="text-align: justify;">Le CPU accepte quatre fréquences, il est actuellement configuré avec la plus rapide : 2,4GHz. On peut la modifier ainsi.</p>
<pre># <strong>echo userspace &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor </strong>
# <strong>echo 1867000 &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed </strong>
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq </strong>
1867000
#</pre>
<h1>Mesurer les effets de la vitesse CPU</h1>
<p style="text-align: justify;">Nous expliquerons la première ligne de l&#8217;exemple ci-dessus dans le prochain paragraphe.<br />
Pour voir les effets de la configuration précédente, on peut utiliser <a title="compte-iterations.c" href="http://www.blaess.fr/christophe/files/article-2012-04-30/compte-iterations.c">un petit programme</a> qui incrémente un compteur pendant une durée donnée (cinq secondes ici).</p>
<pre><strong>compte-iterations.c :</strong>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/time.h&gt;

int main(void)
{
	struct timeval tv;
	struct timeval tv_debut;
	long long int compteur;

	// Attendre le debut d'une nouvelle seconde
	gettimeofday(&amp; tv_debut, NULL);
	do {
		gettimeofday(&amp; tv, NULL);
	} while (tv.tv_sec == tv_debut.tv_sec);

	// Compter pendant cinq secondes
	compteur = 0;
	tv_debut = tv;
	do {
		compteur ++;
		gettimeofday(&amp; tv, NULL);
	} while (tv.tv_sec &lt; (tv_debut.tv_sec + 5));

	printf("Resultats : %lld\n", compteur);
	return EXIT_SUCCESS;
}</pre>
<p style="text-align: justify;">Essayons de l&#8217;exécuter avec différentes fréquences.</p>
<pre># <strong>echo 1600000 &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed </strong>
# <strong>taskset -pc 1 $$</strong>
pid 9778's current affinity list: 0-3
pid 9778's new affinity list: 1
# <strong>./compte-iterations </strong>
Resultats : 17263876
# <strong>./compte-iterations </strong>
Resultats : 16733090
# <strong>./compte-iterations </strong>
Resultats : 16810727
# <strong>echo 1867000 &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed </strong>
# <strong>./compte-iterations </strong>
Resultats : 19926359
# <strong>./compte-iterations </strong>
Resultats : 19868890
# <strong>./compte-iterations </strong>
Resultats : 19681040
# <strong>echo 2133000 &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed </strong>
# <strong>./compte-iterations </strong>
Resultats : 22517877
# <strong>./compte-iterations </strong>
Resultats : 23028619
# <strong>./compte-iterations </strong>
Resultats : 22310298
# <strong>echo 2400000 &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed </strong>
# <strong>./compte-iterations </strong>
Resultats : 25317042
# <strong>./compte-iterations </strong>
Resultats : 25806554
# <strong>./compte-iterations </strong>
Resultats : 25916774
#</pre>
<p style="text-align: justify;">Nous voyons bien que le nombre d&#8217;itérations fluctue en fonction de la fréquence configurée.</p>
<h1>Configurer le comportement du CPU</h1>
<p style="text-align: justify;">Plutôt qu&#8217;agir directement sur la fréquence, ce qui nécessite de lire la valeur et d&#8217;inscrire celle choisie, le noyau nous propose des comportements, des heuristiques, qu&#8217;il nomme <em>governors</em>, qui vont assurer la modification de la fréquence CPU afin de l&#8217;ajuster aux besoins de l&#8217;utilisateur.<br />
On trouvera des explications plus détaillées sur les <strong>governors</strong> dans <a title="Temps-réel et économie d’énergie (1)" href="http://www.blaess.fr/christophe/2011/03/04/temps-reel-et-economie-d-energie-1/">cet article</a>. Il existe (suivant la configuration du noyau à la compilation) jusqu&#8217;à cinq <em>governors</em>, dont les noms sont visibles ainsi.</p>
<pre># <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_available_governors </strong>
conservative ondemand userspace powersave performance
#</pre>
<p style="text-align: justify;">Leurs comportements :</p>
<ul>
<li style="text-align: justify;"><em>powersave</em> : économiser la batterie (d&#8217;un ordinateur portable) en utilisant la fréquence la plus faible possible.</li>
<li style="text-align: justify;"><em>performance</em> : optimiser la vitesse de traitement en adoptant la fréquence disponible la plus élevée.</li>
<li style="text-align: justify;"><em>ondemand</em> : faire varier la fréquence en fonction de la charge système afin d&#8217;optimiser la vitesse de traitement lorsqu&#8217;il y a une forte demande, tout en économisant la batterie lorsqu&#8217;il y a peu de tâches en cours.</li>
<li style="text-align: justify;"><em>conservative</em> : adopter le même comportement que <em>ondemand</em>, en évitant les modifications trop fréquentes de la vitesse du CPU.</li>
<li style="text-align: justify;"><em>userspace</em> : laisser l&#8217;utilisateur fixer lui-même la vitesse qui lui convient, comme nous l&#8217;avons fait dans les paragraphes précédents.</li>
</ul>
<p style="text-align: justify;">Essayons les effets des principaux <em>governors</em> sur notre programme.</p>
<pre># <strong>echo performance &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor </strong>
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq </strong>
2400000
# <strong>./compte-iterations </strong>
Resultats : 25932762
# <strong>./compte-iterations </strong>
Resultats : 25937413
# <strong>./compte-iterations </strong>
Resultats : 25335626
# <strong>echo powersave &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor </strong>
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq</strong>
1600000
# <strong>./compte-iterations </strong>
Resultats : 17625101
# <strong>./compte-iterations </strong>
Resultats : 16788736
# <strong>./compte-iterations </strong>
Resultats : 17017337
# <strong>echo ondemand &gt; /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor </strong>
# <strong>cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq</strong>
1600000
# <strong>./compte-iterations </strong>
Resultats : 25371803
# <strong>./compte-iterations </strong>
Resultats : 25294453
#<strong> ./compte-iterations </strong>
Resultats : 25920801
#</pre>
<h1>Conclusion</h1>
<p style="text-align: justify;">Il est intéressant de pouvoir configurer facilement la vitesse du processeur indépendamment des fréquences exactes, en employant par exemple une heuristique <em>powersave</em> sur un portable, <em>performance</em> sur un serveur, ou <em>ondemand</em> sur un poste de travail généraliste.</p>
<p style="text-align: justify;">Pour les systèmes embarqués, les choix ne sont pas toujours évidents, comme on peut le voir dans ces articles : <a title="Temps-réel et économie d’énergie (1)" href="http://www.blaess.fr/christophe/2011/03/04/temps-reel-et-economie-d-energie-1/" target="_blank">Temps réel et économie d&#8217;énergie 1</a> et <a title="Temps-réel et économie d’énergie (2)" href="http://www.blaess.fr/christophe/2011/03/11/temps-reel-et-economie-denergie-2/" target="_blank">2</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/04/30/modifier-facilement-la-frequence-processeur/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[ACTU] Simulateur de second tour des présidentielles</title>
		<link>http://www.blaess.fr/christophe/2012/04/23/simulateur-de-second-tour-des-presidentielles/</link>
		<comments>http://www.blaess.fr/christophe/2012/04/23/simulateur-de-second-tour-des-presidentielles/#comments</comments>
		<pubDate>Mon, 23 Apr 2012 11:26:56 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Intérêts divers]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=2033</guid>
		<description><![CDATA[Vous trouverez sur cette page un petit simulateur en javascript qui permet de calculer les résultats du second tour en fonction de votre estimation personnelle des participations et reports de voix des candidats du premier tour.]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Vous trouverez <a title="Simulation second tour" href="http://www.blaess.fr/christophe/simulation-2nd-tour.html">sur cette page</a> un petit simulateur en javascript qui permet de calculer les résultats du second tour en fonction de votre estimation personnelle des participations et reports de voix des candidats du premier tour.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/04/23/simulateur-de-second-tour-des-presidentielles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Traitements parallèles dans un script shell</title>
		<link>http://www.blaess.fr/christophe/2012/04/21/traitements-paralleles-dans-un-script-shell/</link>
		<comments>http://www.blaess.fr/christophe/2012/04/21/traitements-paralleles-dans-un-script-shell/#comments</comments>
		<pubDate>Sat, 21 Apr 2012 11:00:41 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1967</guid>
		<description><![CDATA[Il est rare de devoir utiliser des traitements en tâche de fond dans un script shell. À moins, bien entendu, qu&#8217;il s&#8217;agisse d&#8217;un script de démarrage servant justement à lancer plusieurs traitements en parallèle. Il peut néanmoins être parfois nécessaire de gérer des lancements en arrière-plan, comme cela m&#8217;est arrivé une fois. Situation Un système [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Il est rare de devoir utiliser des traitements en tâche de fond dans un script shell. À moins, bien entendu, qu&#8217;il s&#8217;agisse d&#8217;un script de démarrage servant justement à lancer plusieurs traitements en parallèle.</p>
<p style="text-align: justify;">Il peut néanmoins être parfois nécessaire de gérer des lancements en arrière-plan, comme cela m&#8217;est arrivé une fois.</p>
<h1>Situation</h1>
<p style="text-align: justify;">Un système aéroportuaire de traitement de plots radar m&#8217;envoyait des informations en continu sur un port réseau UDP/IP. Je devais les enregistrer dans des fichiers horodatés. Chaque fichier contenait une heure d&#8217;enregistrement environ. Je devais également, à l&#8217;issue de chaque enregistrement, effectuer une analyse des données qui durait environ cinq minutes.</p>
<h1>Logiciels existants</h1>
<p style="text-align: justify;">Deux logiciels, écrits en C, étaient disponibles.</p>
<p style="text-align: justify;">Le premier, appelons-le <code>enregistreur</code> prenait plusieurs arguments sur sa ligne de commande</p>
<ul>
<li style="text-align: justify;">L&#8217;adresse IPv4 du groupe multicast dans lequel s&#8217;inscrire. Ceci n&#8217;a pas d&#8217;intérêt pour ce qui nous concerne ici, prenons par exemple 224.10.10.10.</li>
<li style="text-align: justify;">Le numéro de port UDP pour recevoir les données. La valeur ne nous concerne pas plus que la précédente. Fixons-la arbitrairement à 2012.</li>
<li style="text-align: justify;">La durée en secondes de l&#8217;enregistrement à réaliser. Pour une durée d&#8217;une heure, nous passerons <code>3600</code>.</li>
<li style="text-align: justify;">Le nom du fichier où stocker l&#8217;enregistrement. Pour que les fichiers soient aisément manipulables par la suite, il est nécessaire que leurs noms contiennent un horodatage compréhensible.</li>
</ul>
<p style="text-align: justify;">Le second logiciel, que nous appellerons <code>statistiques</code> était un outil étudiant les données reçues (qu&#8217;il lisait depuis un fichier), et sortant des éléments d&#8217;information sur sa sortie standard. Ce programme prenait uniquement en argument le nom du fichier d&#8217;enregistrement à traiter.</p>
<p style="text-align: justify;">Ces deux logiciels fonctionnaient très bien, et je devais les enchaîner dans un script shell afin qu&#8217;ils tournent 24H/24H.</p>
<h1>Problème</h1>
<p style="text-align: justify;">Tout d&#8217;abord il fallait générer le nom du fichier en incluant un horodatage lisible. Pour cela le plus simple est souvent de s&#8217;appuyer sur la commande <code>date</code> dont les options permettent facilement de construire une chaîne de caractères en insérant des éléments de date et d&#8217;heure.</p>
<pre>NOM_FICHIER=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")</pre>
<p style="text-align: justify;">La variable <code>NOM_FICHIER</code> contiendra une chaîne de caractères comme <code>enregistrement-LFPG-2012-04-21-09-00.dat</code>.</p>
<p style="text-align: justify;">Le second problème était plus gênant. Si l&#8217;on pouvait tolérer la perte d&#8217;une seconde de messages entre deux enregistrements, il n&#8217;était pas envisageable d&#8217;avoir un &laquo;&nbsp;trou&nbsp;&raquo; de cinq minutes (durée du programme <code>statistiques</code>). Je ne pouvais donc pas utiliser le shéma évident suivant.</p>
<pre>#! /bin/sh
ADRESSE_IP=224.10.10.10
PORT_UDP=2012
while true
do
    # Generer le nom du fichier d'enregistrement
    FICHIER=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")

    # Declencher l'enregistrement
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}"

    # Traiter l'heure ecoulee
    statistiques "${FICHIER}" &gt; fichier-resultat.txt
done</pre>
<p style="text-align: justify;">Il fallait donc paralléliser le traitement statistique et l&#8217;enregistrement des données.</p>
<h1>Première idée</h1>
<p style="text-align: justify;">On peut tout d&#8217;abord imaginer un schéma où le traitement statistique fonctionne en arrière-plan, la boucle principale étant cadencée par l&#8217;enregistreur. En voici un exemple.</p>
<pre>#! /bin/sh
[...]
while true
do
    # Generer le nom du fichier d'enregistrement
    FICHIER=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")

    # Declencher l'enregistrement
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}"

    # Traiter l'heure ecoulee
    statistiques "${FICHIER}" &gt; fichier-resultat.txt <strong>&amp;</strong>
done</pre>
<p style="text-align: justify;">On remarquera, à la fin de la ligne de la commande <code>statistiques</code>, le <code>&amp;</code> qui provoque le passage à l&#8217;arrière-plan. Ceci permet au shell de reprendre immédiatement son exécution en début de boucle et donc de démarrer un nouvel enregistrement.</p>
<p style="text-align: justify;">Cette méthode fonctionnait, mais elle présentait néanmoins un petit inconvénient. A l&#8217;issue du traitement statistique, le fichier de résultats devait être renommé en utilisant un numéro d&#8217;ordre séquentiel et copié dans un répertoire indépendant ; ceci seulement dans le cas où le fichier n&#8217;était pas vide. Il devenait compliqué de réaliser ces opérations dans une exécution en arrière-plan.</p>
<p style="text-align: justify;">L&#8217;idée fut donc d&#8217;inverser le principe précédent, en effectuant l&#8217;enregistrement en arrière-plan et le traitement statistique dans la boucle principale.</p>
<h1>Seconde idée</h1>
<p style="text-align: justify;">Voici un premier aperçu de la nouvelle solution.</p>
<pre>#! /bin/sh
[...]
NUMERO_RESULTAT=1

while true
do
    # Generer le nom du fichier d'enregistrement
    FICHIER=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")

    # Declencher l'enregistrement
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}" <strong>&amp;</strong>

    # Traiter l'heure ecoulee
    statistiques "${FICHIER}" &gt; fichier-resultat.txt

    # si le fichier n'est pas vide
    if [ -s fichier-resultat.txt ]

        # Le renommer et le déplacer
        mv fichier-resultat.txt "resultat-${NUMERO_RESULTAT}.txt"
        mv "resultat-${NUMERO_RESULTAT}.txt"  ~/resultats-statistiques/

        # Incrémenter le numéro d'ordre
        NUMERO_RESULTAT=$(( NUMERO_RESULTAT + 1 ))
    fi
done</pre>
<p style="text-align: justify;">Si cette idée semble séduisante au premier abord, elle ne fonctionne pas du tout en réalité. Lorsqu&#8217;on lance l&#8217;enregistreur en arrière-plan, le traitement statistique s&#8217;exécute en quelques minutes et relance un nouvel enregistreur alors que le précédent n&#8217;est pas terminé, et ainsi de suite.</p>
<p style="text-align: justify;">Pour que cette méthode soit utilisable, il faudrait s&#8217;assurer avant de démarrer un enregistrement que le précédent soit fini. Pour cela, nous allons noter après le lancement en arrière-plan de l&#8217;enregistreur son numéro de processus (<em>PID</em>), et avant le lancement nous attendrons que le processus enregistreur précédent soit terminé.</p>
<p style="text-align: justify;">Le shell nous fournit dans la variable <strong><code>$!</code></strong> le PID du dernier processus lancé en arrière-plan. Ajoutons donc la ligne suivante dans notre code.</p>
<pre>[...]
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}" &amp;
    <strong>PID_ENREGISTREUR=$!</strong>
    # Traiter l'heure ecoulee
    [...]</pre>
<p style="text-align: justify;">Pour attendre la fin d&#8217;un processus lancé par le shell, celui-ci nous propose la commande <strong><code>wait</code></strong> que l&#8217;on peut faire suivre du numéro de PID. Ajoutons cette attente avant le lancement.</p>
<pre>[...]
    <strong>wait ${PID_ENREGISTREUR}</strong>
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}" &amp;
    [...]</pre>
<p style="text-align: justify;">Nous devons bien sûr prendre en considération le démarrage de notre script, où aucun enregistreur ne tourne encore. Pour cela, il suffit d&#8217;initialiser la variable <code>PID_ENREGISTREUR</code> à un numéro impossible de processus (les PID sont toujours supérieurs à zéro) et de la tester avant d&#8217;appeler <code>wait</code>.</p>
<h1>Troisième essai</h1>
<p style="text-align: justify;">Notre code devient :</p>
<pre>#! /bin/sh
ADRESSE_IP=224.10.10.10
PORT_UDP=2012
REPERTOIRE_STATISTIQUES="~/resultats-statistiques/"
PID_ENREGISTREUR=0
while true
do
    # Generer le nom du fichier d'enregistrement
    FICHIER=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")

    # Attendre la fin de l'enregistreur precedent
    if [ $PID_ENREGISTREUR -gt 0 ]
    then
        wait $PID_ENREGISTREUR
    fi
    # Declencher l'enregistrement
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER}" &amp;
    PID_ENREGISTREUR=$!

    # Traiter l'heure ecoulee
    statistiques "${FICHIER}" &gt; fichier-resultat.txt

    # si le fichier n'est pas vide
    if [ -s fichier-resultat.txt ]
        # Le renommer et le deplacer
        mv "fichier-resultat.txt" "resultat-${NUMERO_RESULTAT}.txt"
        mv "resultat-${NUMERO_RESULTAT}.txt" "${REPERTOIRE_STATISTIQUES}"

        # Incrementer le numero d'ordre
        NUMERO_RESULTAT=$(( NUMERO_RESULTAT + 1 ))
    fi
done</pre>
<p style="text-align: justify;">Pourtant, en observant le script, on peut s&#8217;apercevoir d&#8217;un gros défaut : le traitement statistique démarre sur le même fichier que l&#8217;enregistreur en cours ! Il faut bien sûr qu&#8217;il travaille sur l&#8217;enregistrement précédent. Modifions encore notre script.</p>
<h1>Solution</h1>
<pre>#! /bin/sh
ADRESSE_IP=224.10.10.10
PORT_UDP=2012
REPERTOIRE_STATISTIQUES="~/resultats-statistiques/"
PID_ENREGISTREUR=0
FICHIER_COURANT=""
FICHIER_PRECEDENT=""

while true
do
    # Attendre la fin de l'enregistreur precedent
    if [ ${PID_ENREGISTREUR} -gt 0 ]
    then
        wait ${PID_ENREGISTREUR}
        FICHIER_PRECEDENT="${FICHIER_COURANT}"
    fi

    # Generer le nom du nouvel enregistrement
    FICHIER_COURANT=$(date +"enregistrement-LFPG-%Y-%m-%d-%H-%M.dat")

    # Lancer l'enregistreur en arriere-plan
    enregistreur ${ADRESSE_IP} ${PORT_UDP} 3600 "${FICHIER_COURANT}" &amp;
    PID_ENREGISTREUR=$!

    # Traiter l'enregistrement de l'heure ecoulee
    statistiques "${FICHIER_PRECEDENT}" &gt; fichier-resultat.txt

    # si le resultat n'est pas vide
    if [ -s fichier-resultat.txt ]
        # Le renommer et le deplacer
        mv "fichier-resultat.txt" "resultat-${NUMERO_RESULTAT}.txt"
        mv "resultat-${NUMERO_RESULTAT}.txt" "${REPERTOIRE_STATISTIQUES}"

        # Incrementer le numero d'ordre
        NUMERO_RESULTAT=$(( NUMERO_RESULTAT + 1 ))
    fi
done</pre>
<p style="text-align: justify;">Cette fois le script est correct et fonctionne.</p>
<h1>Conclusion</h1>
<p style="text-align: justify;">Nous voyons que l&#8217;encadrement par un script shell de programmes existants n&#8217;est pas toujours évident surtout lorsqu&#8217;il existe une dépendance temporelle entre eux (synchronisation). Ici, certains points du script ont été ignorés (gestion des erreurs d&#8217;exécution comme la saturation du disque par exemple), mais il nous a quand même fallu quatre versions pour obtenir enfin un programme correct. On retiendra égtalement que le soin apporté à la lisibilité et à la clarté du script est un gage de facilité de maintenance ultérieure, surtout lorsque l&#8217;algorithme global sort quelque peu de l&#8217;ordinaire (parallélisme par exemple).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/04/21/traitements-paralleles-dans-un-script-shell/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[NOSTALGIE] Le ZX-81</title>
		<link>http://www.blaess.fr/christophe/2012/04/05/nostalgie-le-zx-81/</link>
		<comments>http://www.blaess.fr/christophe/2012/04/05/nostalgie-le-zx-81/#comments</comments>
		<pubDate>Wed, 04 Apr 2012 23:04:47 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Intérêts divers]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1842</guid>
		<description><![CDATA[Voilà, c&#8217;est sûr, mon vieux ZX-81 ne fonctionne plus… J&#8217;ai voulu en avoir le cœur net, et j&#8217;ai tenté de le remettre en marche pour fêter le trentième anniversaire de son acquisition. En vain, il n&#8217;y a aucun signe de fonctionnement &#8211; bien que l&#8217;alimentation soit toujours correcte. En outre le câble plat qui reliait [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Voilà, c&#8217;est sûr, mon vieux ZX-81 ne fonctionne plus…</p>
<div id="attachment_1883" class="wp-caption alignright" style="width: 310px"><a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/04/ZX-81.jpg"><img class="size-medium wp-image-1883 " title="ZX-81" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/04/ZX-81-300x185.jpg" alt="ZX-81" width="300" height="185" /></a><p class="wp-caption-text">ZX-81, imprimante et extension 16ko</p></div>
<p style="text-align: justify;">J&#8217;ai voulu en avoir le cœur net, et j&#8217;ai tenté de le remettre en marche pour fêter le trentième anniversaire de son acquisition. En vain, il n&#8217;y a aucun signe de fonctionnement &#8211; bien que l&#8217;alimentation soit toujours correcte. En outre le câble plat qui reliait le clavier à la carte à microprocesseur à mal supporté l&#8217;usure du temps et s&#8217;est cassé net en plusieurs endroits ce qui me paraît difficilement réparable.</p>
<p style="text-align: justify;">Tant pis, il ne se rallumera pas, mais cela ne m&#8217;empêchera aucunement de le conserver sur une étagère avec un certain nombre d&#8217;autres rescapés de la même époque. J&#8217;éprouve une affection particulière pour ce petit ordinateur sur lequel j&#8217;ai découvert la programmation en basic et en assembleur.</p>
<p style="text-align: justify;">Pour ceux qui n&#8217;ont pas connu le ZX-81 (TS-1000 dans sa version américaine), il faut savoir que c&#8217;était le premier ordinateur vraiment grand public, et que les choix économiques rigoureux faits par son concepteur (<a title="Clive Sinclair sur Wikipedia" href="http://fr.wikipedia.org/wiki/Clive_Sinclair" target="_blank">Sir Clive Sinclair</a>) en rendait l&#8217;utilisation assez&#8230; particulière. Tout d&#8217;abord le gros défaut de cet ordinateur était le manque de fiabilité des connexions sur le bus d&#8217;extension (où l&#8217;on branchait entre autre le bloc d&#8217;extension mémoire) ce qui provoquait des coupures inopinées d&#8217;alimentation en cas de mouvement de l&#8217;unité centrale, avec toutes les pertes de données que l&#8217;on peut imaginer.</p>
<p style="text-align: justify;">Le système reposait sur un processeur huit bits, le Zilog Z-80-A, cadencé  à 3.5 MHz, et sur une mémoire RAM de 1 kilo-octet extensible à 16ko, voire 64ko pour les plus fortunés. Et aussi étonnant que cela puisse paraître, nous disposions de nombreux programmes (jeux, utilitaires, programmation, etc.) qui tenaient dans ces dimensions réduites de mémoire. La sauvegarde de la mémoire se faisait sur des cassettes audios par l&#8217;intermédiaire d&#8217;un magnétophone externe, le chargement d&#8217;un programme de quelques kilo-octets pouvant durer plusieurs longues minutes avec une fiabilité plutôt aléatoire.</p>
<p style="text-align: justify;">Dès la mise sous tension, l&#8217;utilisateur disposait d&#8217;un <em>prompt</em> permettant de saisir des lignes de codes dans une version simplifiée du langage <a title="Le langage BASIC" href="http://fr.wikipedia.org/wiki/BASIC" target="_blank">BASIC</a>. Deux instructions, PEEK et POKE, permettaient de lire ou d&#8217;écrire des octets directement dans la mémoire et la fonction USR autorisait l&#8217;exécution d&#8217;un code à une adresse quelconque, généralement après y avoir inséré du code assembleur personnel.</p>
<div id="attachment_1847" class="wp-caption alignleft" style="width: 271px"><a href="http://www.blaess.fr/christophe/2012/04/05/nostalgie-le-zx-81/capture-3d-maze/" rel="attachment wp-att-1847"><img class="size-full wp-image-1847 " title="Capture 3D Monster Maze" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/04/capture-3d-maze.png" alt="Capture 3D Monster Maze" width="261" height="196" /></a><p class="wp-caption-text">3D Monster Maze</p></div>
<p style="text-align: justify;">Les capacités vidéo étaient très limitées : 32 x 24 caractères monochromes. 16 caractères spéciaux étaitent constitués d&#8217;une matrice de quatre petits carrés, qui pouvaient se combiner pour offrir des graphiques avec la résolution de 64&#215;48 pixels ! L&#8217;ingéniosité des programmeurs permettait de disposer quand même de jeux graphiques, comme le fameux &laquo;&nbsp;<em>3D Monster Maze&nbsp;&raquo;</em> dans lequel le joueur se trouvait plongé dans un labyrinthe chassé par un tyrannosaure. J&#8217;ai pu en prendre la copie d&#8217;écran ci-contre sur un <a title="Émulateur de ZX-81" href="http://www.vavasour.ca/jeff/ts1000/" target="_blank">émulateur en-ligne</a>. Mes jeux favoris étaient également &laquo;&nbsp;<em>Space Invaders</em>&nbsp;&raquo; et &laquo;&nbsp;<em>Flight Simulation</em>&nbsp;&raquo; (copie d&#8217;écran ci-dessous) ainsi que tous les programmes que l&#8217;on trouvait sous forme de listing dans les revues de l&#8217;époque.</p>
<div id="attachment_1860" class="wp-caption alignright" style="width: 270px"><a href="http://www.blaess.fr/christophe/2012/04/05/nostalgie-le-zx-81/capture-flight-simulation/" rel="attachment wp-att-1860"><img class="size-full wp-image-1860" title="Flight Simulation" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/04/capture-flight-simulation.png" alt="Flight Simulation" width="260" height="196" /></a><p class="wp-caption-text">Flight Simulation</p></div>
<p style="text-align: justify;">J&#8217;ai appris sur cet ordinateur la programmation en BASIC, en écrivant notamment quelques outils de calculs pour l&#8217;astronomie (à usage personnel), et la programmation assembleur pour des jeux &#8211; inspirés de Pac-man ou Centipède &#8211; qui n&#8217;ont à ma connaissance jamais été diffusés en dehors d&#8217;un cercle très restreint d&#8217;amis possesseurs de la même machine.</p>
<p style="text-align: justify;"> Il faut souligner la pédagogie du manuel (en français) du ZX-81 qui permettait de découvrir progressivement les principes de la programmation, et la bonne qualité des livres &#8211; disponibles en français également &#8211; pour approfondir l&#8217;étude de l&#8217;assembleur par exemple.</p>
<p style="text-align: justify;">Même s&#8217;il ne redémarre pas, ce petit ordinateur continuera à trôner sur une étagère de mon bureau, attirant les regards surpris, amusés et parfois nostalgiques des visiteurs qui l&#8217;aperçoivent.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/04/05/nostalgie-le-zx-81/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[ACTU] Mais pourquoi la Freebox reste-t-elle à l&#8217;heure d&#8217;hiver ?</title>
		<link>http://www.blaess.fr/christophe/2012/03/27/actu-mais-pourquoi-la-freebox-reste-t-elle-a-l-heure-d-hiver/</link>
		<comments>http://www.blaess.fr/christophe/2012/03/27/actu-mais-pourquoi-la-freebox-reste-t-elle-a-l-heure-d-hiver/#comments</comments>
		<pubDate>Tue, 27 Mar 2012 08:49:54 +0000</pubDate>
		<dc:creator>cpb</dc:creator>
				<category><![CDATA[Actualité]]></category>
		<category><![CDATA[Embarqué]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.blaess.fr/christophe/?p=1833</guid>
		<description><![CDATA[Comme beaucoup d&#8217;entre vous le savent déjà, la Freebox v.6 a raté son passage à l&#8217;heure d&#8217;été. Ce matin, alors que mon PC, ma montre et mon téléphone affichent 09:44, la Freebox posée à côté de mon bureau affiche encore 08:44. Au-delà du fait divers amusant, source de plaisanteries et de sarcasmes sur Twitter, je [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Comme beaucoup d&#8217;entre vous le savent déjà, la Freebox v.6 a raté son passage à l&#8217;heure d&#8217;été. Ce matin, alors que mon PC, ma montre et mon téléphone affichent 09:44, la Freebox posée à côté de mon bureau affiche encore 08:44. Au-delà du fait divers amusant, source de plaisanteries et de sarcasmes sur Twitter, je trouve cette mésaventure intéressante, car elle trouve son origine dans des logiciels que connaissent bien les amateurs de Linux embarqué !<a href="http://www.blaess.fr/christophe/wp-content/uploads/2012/03/IMG_20120327_094456.jpg"><img class="alignright size-medium wp-image-1834" title="Freebox a l'heure d'hiver" src="http://www.blaess.fr/christophe/wp-content/uploads/2012/03/IMG_20120327_094456-300x207.jpg" alt="Freebox a l'heure d'hiver" width="300" height="207" /></a></p>
<p style="text-align: justify;">La bibliothèque <a title="uClibc" href="http://www.uclibc.org" target="_blank">µClibc</a> regroupe les fonctionnalités communes dont les applications de haut-niveau ont besoin (par exemple des traitements de chaîne de caractères, des fonctions de calcul, des méthodes d&#8217;accès aux fichiers, etc.) en fournissant les points d&#8217;entrée pour le noyau Linux (les appels-système). La particularité de la µClibc par rapport aux bibliothèques &laquo;&nbsp;C&nbsp;&raquo; plus classiques comme la <a title="Gnu Glibc" href="http://www.gnu.org/software/libc/" target="_blank">Gnu Glibc</a> est d&#8217;être écrite en essayant de réduite au maximum la taille du code exécutable, et de pouvoir ainsi être employée dans les systèmes embarqués avec de fortes contraintes de place mémoire.</p>
<p style="text-align: justify;">La plupart des systèmes embarqués construits avec des outils comme Buildroot (comme ceux que nous avons mis en oeuvre dans <a title="Construire son système personnel sur une carte Pandaboard (1)" href="http://www.blaess.fr/christophe/2011/05/06/construire-son-systeme-personnel-sur-une-carte-pandaboard/" target="_blank">plusieurs articles</a>) s&#8217;appuient sur la µClibc, et la Freebox v.6 ne fait apparemment pas exception, même si la bibliothèque n&#8217;est pas explicitement mentionnée dans la liste de logiciels libres décrites <a title="http://floss.freebox.fr/" href="http://floss.freebox.fr/" target="_blank">ici</a>.</p>
<p style="text-align: justify;">Dans la dernière version stable de la µClibc (0.9.33 du 1er février), un bug a été corrigé, comme cela est mentionné dans sa page de <a title="http://uclibc.org/downloads/ChangeLog-0.9.32.1_0.9.33" href="http://uclibc.org/downloads/ChangeLog-0.9.32.1_0.9.33" target="_blank"><em>ChangeLog</em></a>. Voici la portion qui nous intéresse :</p>
<pre style="padding-left: 30px;">commit 47f3da1cf49377c25772bb54d07db55225bbb142
Author: Guillaume Bourcier
Date:   Tue Oct 11 13:45:33 2011 +0200

    <strong>libc: fix daylight saving time handling</strong>

    The algorithm computing daylight saving time incorrectly adds a day for
    each month after January for leap years. The clock shift from/to DST can
    be delayed if the last Sunday of a transition month is exactly seven
    days before the first of the following month.

    This change adds a day for the February month only.

    Signed-off-by: Guillaume Bourcier
    Signed-off-by: Richard Braun
    Signed-off-by: Carmelo Amoroso</pre>
<p style="text-align: justify;">Pour les années bissextiles (comme 2012), l&#8217;algorithme de calcul de la date de passage à l&#8217;heure d&#8217;été ajoutait par erreur un jour au mois de février.</p>
<p style="text-align: justify;">Ainsi le dernier dimanche du mois de mars (ce qui définit officiellement la date du changement d&#8217;heure) était considéré comme le dimanche 31 mars (alors que le 31 mars tombe en réalité un samedi). Visiblement le firmware de la Freebox ne contient pas encore cette correction, aussi pense-t-elle que le changement horaire doit avoir lieu dimanche prochain.</p>
<p style="text-align: justify;">Si cette explication est la bonne, nos Freebox devraient basculer en heure d&#8217;été dimanche 1er avril à 02:00. Peut être Free va-t-il plaider l&#8217;hypothèse d&#8217;un poisson d&#8217;avril sophistiqué et anticipé ? À moins qu&#8217;une mise à jour du firmware intervienne d&#8217;ici là.</p>
<p style="text-align: justify;">Une touche d&#8217;humour supplémentaire : dans le message <em>ChangeLog</em> ci-dessus j&#8217;ai supprimé la fin des adresses mails des personnes mentionnées pour leur éviter le spam. Il faut quand même savoir que l&#8217;adresse de G.Bourcier qui a détecté et corrigé le bug est &laquo;&nbsp;Free.fr&nbsp;&raquo; !</p>
<p style="text-align: justify;">[AJOUT le 30/03/2012]: Free annonce avoir modifié le firmware du Freebox Server (version 1.1.5) pour corriger l&#8217;erreur. Il faut le redémarrer pour que la mise à jour ait lieu. Je ne l&#8217;ai pas fait sur ma Freebox pour vérifier dimanche si le changement horaire est bien décalé d&#8217;une semaine.</p>
<p style="text-align: justify;">[AJOUT le 01/04/2012]: Comme on pouvait s&#8217;y attendre, le changement horaire a eu lieu cette nuit et l&#8217;afficheur de la Freebox indique bien l&#8217;heure correcte. Il est temps de faire la mise à jour du firmware, car sa <a title="http://www.freebox-v6.fr/index.php/blog/article/209/Firmware-1.1.5-pour-la-Freebox-Server-et-correction-du-bug-de-lheure" href="http://www.freebox-v6.fr/index.php/blog/article/209/Firmware-1.1.5-pour-la-Freebox-Server-et-correction-du-bug-de-lheure">nouvelle version</a> intègre plusieurs corrections.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.blaess.fr/christophe/2012/03/27/actu-mais-pourquoi-la-freebox-reste-t-elle-a-l-heure-d-hiver/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

