Lecture 16 – A device driver: Difference between revisions

Copyright © 2017–2023 J. M. Spivey
Jump to navigation Jump to search
(Created page with "The Nordic chip on the microbit has a temperature sensor that measures the temperature of the processor die in units of 1/4° Celcius. The device consists of an analog cir...")
 
No edit summary
Line 1: Line 1:
The Nordic chip on the microbit has a temperature sensor that measures the temperature of the processor die in units of 1/4° Celcius.  The device consists of an analog circuit that exploits some temperature-dependent property of silicon, together with a dedicated A-to-D converter that digitises a reading. We will aim to write a device driver for the sensor, and provide a function
The Nordic chip on the microbit has a temperature sensor that measures the temperature of the processor die in units of 1/4° Celcius.  The device consists of an analog circuit that exploits some temperature-dependent property of silicon, together with a dedicated A-to-D converter that digitises a reading. The A-to-D converter takes some time to settle, so there is a protocol where the processor initiates a reading, then must wait for an event that can be signalled by interrupt before getting the result.  We will aim to write a device driver for the sensor, and provide a function
  int get_temp(void)
  int temp_reading(void)
that can be called from any process to get an up-to-date temperature reading.
that can be called from any process to get an up-to-date temperature reading.


Reasons for having a separate device driver process:
Reasons for having a separate device driver process:
*We can manage ''concurrent access'' from multiple processes and prevent a new reading being initiated before the previous one is finished.
*We can manage ''concurrent access'' from multiple processes and prevent a new reading being initiated before the previous one is finished.
*We can connect to the interrupt from the device and react
*We can connect to the interrupt from the device and react when a reading is complete.
 
==The hardware==
Two pages from the nRF51822 manual describe the hardware of the temperature sensor.  All the device registers are addressible at offsets from the base address @TEMP = 0x4000C000@.
* There is an 'task' @START = 0x000@ that initiates a reading when triggered with @TEMP.START = 1@.
* There is an 'event' @DATARDY = 0x100@ that indicates when the reading is ready (with @TEMP.DATARDY == 1@).
* There is an interrupt enable register @INTEN = 0x300@ in which bit @TEMP_INT_DATARDY = 0@ enables the interrupt on the @DATARDY@ event.
* There is a data register @TEMP = 0x508@ that contains the value of the reading when ready.
 
==The driver process==
Because we will deal with each request -- starting the reading, waiting for it to be ready, then retrieving the value -- before accepting another request, we can write the driver process in a particularly simple way, where the server loop deals with one complete request for each iteration.
 
The interface between the client and the device driver is a function @temp_reading()@ that sends a request to the driver, awaits a reply, and returns the temperature reading (still in units of 1/4°).  For the ''remote procedure call'' idiom of sending a request and waiting for the reply, we can use the @sendrec@ system call.
<pre>
int temp_reading(void)
{
    message m;
    sendrec(TEMP_TASK, REQUEST, &m);
    return m.int1;
}
</pre>
 
<pre>
static void temp_task(int arg)
{
    message m;
    int temp, client;
 
    TEMP.INTEN = BIT(TEMP_INT_DATARDY);
    connect(TEMP_IRQ);
    enable_irq(TEMP_IRQ);
 
    while (1) {
        receive(ANY, &m);
 
        switch (m.type) {
        case REQUEST:
            client = m.sender;
 
            TEMP.START = 1;
            receive(INTERRUPT, NULL);
            assert(TEMP.DATARDY);
            temp = TEMP.VALUE;
            TEMP.DATARDY = 0;
            clear_pending(TEMP_IRQ);
            enable_irq(TEMP_IRQ);
 
            m.int1 = temp;
            send(client, REPLY, &m);
            break;
 
        default:
            badmesg(m.type);
        }
    }
}
</pre>

Revision as of 12:39, 2 March 2022

The Nordic chip on the microbit has a temperature sensor that measures the temperature of the processor die in units of 1/4° Celcius. The device consists of an analog circuit that exploits some temperature-dependent property of silicon, together with a dedicated A-to-D converter that digitises a reading. The A-to-D converter takes some time to settle, so there is a protocol where the processor initiates a reading, then must wait for an event that can be signalled by interrupt before getting the result. We will aim to write a device driver for the sensor, and provide a function

int temp_reading(void)

that can be called from any process to get an up-to-date temperature reading.

Reasons for having a separate device driver process:

  • We can manage concurrent access from multiple processes and prevent a new reading being initiated before the previous one is finished.
  • We can connect to the interrupt from the device and react when a reading is complete.

The hardware

Two pages from the nRF51822 manual describe the hardware of the temperature sensor. All the device registers are addressible at offsets from the base address TEMP = 0x4000C000.

  • There is an 'task' START = 0x000 that initiates a reading when triggered with TEMP.START = 1.
  • There is an 'event' DATARDY = 0x100 that indicates when the reading is ready (with TEMP.DATARDY == 1).
  • There is an interrupt enable register INTEN = 0x300 in which bit TEMP_INT_DATARDY = 0 enables the interrupt on the DATARDY event.
  • There is a data register TEMP = 0x508 that contains the value of the reading when ready.

The driver process

Because we will deal with each request – starting the reading, waiting for it to be ready, then retrieving the value – before accepting another request, we can write the driver process in a particularly simple way, where the server loop deals with one complete request for each iteration.

The interface between the client and the device driver is a function temp_reading() that sends a request to the driver, awaits a reply, and returns the temperature reading (still in units of 1/4°). For the remote procedure call idiom of sending a request and waiting for the reply, we can use the sendrec system call.

int temp_reading(void)
{
    message m;
    sendrec(TEMP_TASK, REQUEST, &m);
    return m.int1;
}
static void temp_task(int arg)
{
    message m;
    int temp, client;

    TEMP.INTEN = BIT(TEMP_INT_DATARDY);
    connect(TEMP_IRQ);
    enable_irq(TEMP_IRQ);

    while (1) {
        receive(ANY, &m);

        switch (m.type) {
        case REQUEST:
            client = m.sender;

            TEMP.START = 1;
            receive(INTERRUPT, NULL);
            assert(TEMP.DATARDY);
            temp = TEMP.VALUE;
            TEMP.DATARDY = 0;
            clear_pending(TEMP_IRQ);
            enable_irq(TEMP_IRQ);

            m.int1 = temp;
            send(client, REPLY, &m);
            break;

        default:
            badmesg(m.type);
        }
    }
}