Dispositivo RTThread IO e aprendizagem de driver

1. Estrutura do modelo de dispositivo IO

Existem três níveis:

Insira a descrição da imagem aqui

  • A camada de gerenciamento de dispositivo
    realiza o encapsulamento do driver de dispositivo.
    O programa aplicativo acessa o dispositivo subjacente por meio da interface padrão fornecida pela camada do dispositivo de E / S, e a atualização e substituição do driver do dispositivo não afetará o aplicativo da camada superior.
    Desta forma, o código relacionado à operação de hardware do dispositivo pode existir independentemente do aplicativo, e ambas as partes precisam apenas prestar atenção à realização de suas respectivas funções, reduzindo assim o acoplamento e a complexidade do código, e melhorando o confiabilidade do sistema.

  • A camada de estrutura do driver de dispositivo é
    uma abstração de drivers de dispositivo de hardware semelhantes, extraindo as mesmas partes de drivers de dispositivo de hardware semelhantes de diferentes fabricantes, deixando diferentes partes fora da interface e implementando-as pelo driver.

  • Camada de driver de dispositivo
    Um grupo de programas que conduz dispositivos de hardware para funcionar e realizar a função de acessar dispositivos de hardware.
    Ele é responsável por criar e registrar dispositivos de E / S. Para dispositivos com lógica de operação simples, você pode registrar diretamente o dispositivo no gerenciador de dispositivos de E / S sem passar pela camada de estrutura do driver de dispositivo. O diagrama de sequência é mostrado na figura abaixo , e os dois principais são os seguintes: ponto:

  1. O modelo define o aparelho de acionamento do dispositivo, um exemplo de um dispositivo inclui hardware para criar recursos de acesso, os dispositivos rt_device_register()registrados na interface do gerenciador de dispositivo de E / S.
  2. Faça a aplicação rt_device_find()para encontrar a interface do dispositivo e, em seguida, use a interface de gerenciamento do dispositivo de E / S para acessar o hardware.

Quando não há camada de estrutura de driver de dispositivo:
Quando não há camada de estrutura de driver de dispositivo

Para outros dispositivos, como watchdogs, a instância de dispositivo criada será registrada primeiro na estrutura de driver de dispositivo correspondente e, em seguida, a estrutura de driver de dispositivo será registrada no gerenciador de dispositivo de E / S. Existem três pontos principais:

  1. O dispositivo de driver de dispositivo de watchdog de acordo com a definição do modelo de watchdog, criar uma instância do dispositivo inclui recursos de acesso de watchdog de hardware e pelo dispositivo de watchdog rt_hw_watchdog_register()registrado nas interfaces de estrutura de driver de dispositivo de watchdog.
  2. Quadro de rt_device_register()watchdog por uma interface de driver de dispositivo para o watchdog registrar o gerenciador de dispositivos de E / S do dispositivo.
  3. O programa aplicativo acessa o hardware do dispositivo watchdog por meio da interface de gerenciamento do dispositivo de E / S.

Modelo de driver de dispositivo IO completo:
Modelo de driver de dispositivo IO completo

2. Modelo de classe de dispositivo IO

Diagrama de relacionamento de classe de dispositivo:
Diagrama de classe de dispositivo

Definição da estrutura da classe do dispositivo:

struct rt_device
{
    
    
    struct rt_object          parent;        /* 内核对象基类 */
    enum rt_device_class_type type;          /* 设备类型 */
    rt_uint16_t               flag;          /* 设备参数 */
    rt_uint16_t               open_flag;     /* 设备打开标志 */
    rt_uint8_t                ref_count;     /* 设备被引用次数 */
    rt_uint8_t                device_id;     /* 设备 ID,0 - 255 */

    /* 数据收发回调函数 */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

    const struct rt_device_ops *ops;    /* 设备操作方法 */

    /* 设备的私有数据 */
    void *user_data;
};
typedef struct rt_device *rt_device_t;

3. Tipos de dispositivos I / O

RT-Thread suporta uma variedade de tipos de dispositivos de E / S, os principais tipos de dispositivos são os seguintes:

RT_Device_Class_Char             /* 字符设备       */
RT_Device_Class_Block            /* 块设备         */
RT_Device_Class_NetIf            /* 网络接口设备    */
RT_Device_Class_MTD              /* 内存设备       */
RT_Device_Class_RTC              /* RTC 设备        */
RT_Device_Class_Sound            /* 声音设备        */
RT_Device_Class_Graphic          /* 图形设备        */
RT_Device_Class_I2CBUS           /* I2C 总线设备     */
RT_Device_Class_USBDevice        /* USB device 设备  */
RT_Device_Class_USBHost          /* USB host 设备   */
RT_Device_Class_SPIBUS           /* SPI 总线设备     */
RT_Device_Class_SPIDevice        /* SPI 设备        */
RT_Device_Class_SDIO             /* SDIO 设备       */
RT_Device_Class_Miscellaneous    /* 杂类设备        */

4. Crie e registre dispositivos de I / O

A camada de driver é responsável por criar instâncias de dispositivos e registrá-los no gerenciador de dispositivos de E / S,

4.1, crie um dispositivo

  • A instância do dispositivo pode ser criada por declaração estática
  • Você também pode usar a seguinte interface para criação dinâmica:
rt_device_t rt_device_create(int type, int attach_size);
parâmetro Descrição
modelo Tipo de dispositivo, pode assumir o valor do tipo de dispositivo listado na seção anterior
attach_size Tamanho dos dados do usuário
Retorna ——
Alça do dispositivo Criado com sucesso
RT_NULL Falha na criação, falha na alocação de memória dinâmica

Ao invocar a interface, o sistema atribuirá um bloco de controle de dispositivo dinâmico da memória heap, o tamanho struct rt_devicee o attach_sizetipo e o tipo de dispositivo do conjunto de parâmetros. Depois que o dispositivo é criado, ele precisa implementar seu método de acesso ao hardware.

struct rt_device_ops
{
    
    
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};

A descrição de cada método de operação é mostrada na tabela a seguir:

Nome do método Descrição do método
iniciar Inicialize o dispositivo. Depois que o dispositivo é inicializado, o sinalizador do bloco de controle do dispositivo será definido para o estado ativado (RT_DEVICE_FLAG_ACTIVATED). Se o sinalizador de sinalização no bloco de controle do dispositivo foi definido para um estado ativo, ele retornará imediatamente quando a interface de inicialização for executada novamente sem reinicialização.
abrir Ligue o dispositivo. Alguns dispositivos não são ligados e começam a funcionar assim que o sistema é iniciado, ou o dispositivo precisa enviar e receber dados, mas se o aplicativo da camada superior não estiver pronto, o dispositivo não deve ser habilitado por padrão e começar a receber dados . Portanto, é recomendável habilitar o dispositivo apenas quando a interface aberta for chamada ao gravar o driver de baixo nível.
fechar Desligue o dispositivo. Quando o dispositivo é ligado, o bloco de controle do dispositivo mantém uma contagem aberta e executa uma operação + 1 quando o dispositivo é ligado e executa uma operação -1 quando o dispositivo é desligado. Quando o contador torna-se 0, o valor real operação de fechamento é executada.
leitura Leia os dados do dispositivo. O parâmetro pos é o deslocamento dos dados lidos, mas alguns dispositivos não precisam necessariamente especificar o deslocamento, como dispositivos seriais, o driver do dispositivo deve ignorar este parâmetro. Para dispositivos de bloco, pos e tamanho são baseados no tamanho do bloco de dados do dispositivo de bloco. Por exemplo, o tamanho do bloco de dados do dispositivo de bloco é 512, e pos = 10, size = 2 nos parâmetros, então o driver deve retornar o 10º bloco no dispositivo (começando do 0º bloco), um total de 2 blocos De dados. O tipo retornado por esta interface é rt_size_t, que é o número de bytes ou blocos lidos. Normalmente, o valor de tamanho no parâmetro deve ser retornado. Se ele retornar zero, defina o valor errno correspondente.
Escreva Grave dados no dispositivo. O parâmetro pos é o deslocamento dos dados gravados. Semelhante às operações de leitura, para dispositivos de bloco, pos e tamanho são baseados no tamanho do bloco de dados do dispositivo de bloco. O tipo retornado por esta interface é rt_size_t, que é o número de bytes ou blocos de dados realmente gravados. Normalmente, o valor de tamanho no parâmetro deve ser retornado. Se ele retornar zero, defina o valor errno correspondente.
ao controle Controle o dispositivo de acordo com os comandos cmd. Os comandos são frequentemente implementados personalizados por vários drivers de dispositivo subjacentes. Por exemplo, o parâmetro RT_DEVICE_CTRL_BLK_GETGEOME significa obter as informações de tamanho do dispositivo de bloco.

Quando um dispositivo criado dinamicamente não é mais necessário, ele pode ser destruído pela seguinte função:

void rt_device_destroy(rt_device_t device);
parâmetro Descrição
dispositivo Alça do dispositivo
Retorna não

4.2. Registre o dispositivo

Depois que o dispositivo é criado, ele precisa ser registrado no Gerenciador de Dispositivos de E / S para que o aplicativo possa acessá-lo. A função de registro do dispositivo é a seguinte:

rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags); 
parâmetro Descrição
dev Alça do dispositivo
nome O nome do dispositivo, o comprimento máximo do nome do dispositivo é especificado pela macro RT_NAME_MAX definida em rtconfig.h, e a parte extra será cortada automaticamente
bandeiras Sinalizador de modo de dispositivo
Retorna ——
RT_EOK Sucesso no registro
RT_ERROR O registro falhou, dev está vazio ou o nome já existe

O parâmetro flags suporta os seguintes parâmetros (você pode usar ou para suportar uma variedade de parâmetros), de fato, a autoridade de operação do dispositivo é definida:

#define RT_DEVICE_FLAG_RDONLY       0x001 /* 只读 */
#define RT_DEVICE_FLAG_WRONLY       0x002 /* 只写  */
#define RT_DEVICE_FLAG_RDWR         0x003 /* 读写  */
#define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */
#define RT_DEVICE_FLAG_STANDALONE   0x008 /* 独立   */
#define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 挂起  */
#define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */
#define RT_DEVICE_FLAG_INT_RX       0x100 /* 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */
#define RT_DEVICE_FLAG_INT_TX       0x400 /* 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 发送 */

4.3, faça logoff do dispositivo

rt_err_t rt_device_unregister(rt_device_t dev);
parâmetro Descrição
dispositivo Alça do dispositivo
Retorna ——
RT_EOK sucesso

O seguinte código de exemplo de dispositivo watchdog registrado, chama rt_hw_watchdog_register()os dispositivos de interface rt_device_register()são registrados para a interface do gerenciador de dispositivo de E / S.

const static struct rt_device_ops wdt_ops =
{
    
    
    rt_watchdog_init,
    rt_watchdog_open,
    rt_watchdog_close,
    RT_NULL,
    RT_NULL,
    rt_watchdog_control,
};

rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,
                                 const char                *name,
                                 rt_uint32_t                flag,
                                 void                      *data)
{
    
    
    struct rt_device *device;
    RT_ASSERT(wtd != RT_NULL);

    device = &(wtd->parent);

    device->type        = RT_Device_Class_Miscellaneous;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    device->ops         = &wdt_ops;
    device->user_data   = data;

    /* register a character device */
    return rt_device_register(device, name, flag);
}

问题:系统是在哪里调用的hw_watchdog_register接口?

5、访问 I/O 设备

I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示:

映射关系:
Mapeamento de relações

I/O设备管理接口放在内核的device.c文件中。

5.1、查找设备

应用程序根据设备名称获取设备句柄,进而可以操作设备。查找设备函数如下所示:

rt_device_t rt_device_find(const char* name);
参数 描述
name 设备名称
返回 ——
设备句柄 查找到对应设备将返回相应的设备句柄
RT_NULL 没有找到相应的设备对象

5.2、初始化设备

获得设备句柄后,应用程序可使用如下函数对设备进行初始化操作:

rt_err_t rt_device_init(rt_device_t dev);
参数 描述
dev 设备句柄
返回 ——
RT_EOK 设备初始化成功
错误码 设备初始化失败

注意事项:

当一个设备已经初始化成功后,调用这个接口将不再重复做初始化 0。

5.3、打开和关闭设备

通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
参数 描述
dev 设备句柄
oflags 设备打开模式标志
返回 ——
RT_EOK 设备打开成功
-RT_EBUSY 如果设备注册时指定的参数中包括 RT_DEVICE_FLAG_STANDALONE 参数,此设备将不允许重复打开
其他错误码 设备打开失败

oflags 支持以下的参数:

#define RT_DEVICE_OFLAG_CLOSE 0x000   /* 设备已经关闭(内部使用)*/
#define RT_DEVICE_OFLAG_RDONLY 0x001  /* 以只读方式打开设备 */
#define RT_DEVICE_OFLAG_WRONLY 0x002  /* 以只写方式打开设备 */
#define RT_DEVICE_OFLAG_RDWR 0x003    /* 以读写方式打开设备 */
#define RT_DEVICE_OFLAG_OPEN 0x008    /* 设备已经打开(内部使用)*/
#define RT_DEVICE_FLAG_STREAM 0x040   /* 设备以流模式打开 */
#define RT_DEVICE_FLAG_INT_RX 0x100   /* 设备以中断接收模式打开 */
#define RT_DEVICE_FLAG_DMA_RX 0x200   /* 设备以 DMA 接收模式打开 */
#define RT_DEVICE_FLAG_INT_TX 0x400   /* 设备以中断发送模式打开 */
#define RT_DEVICE_FLAG_DMA_TX 0x800   /* 设备以 DMA 发送模式打开 */

注意事项:

如果上层应用程序需要设置设备的接收回调函数,则必须以 RT_DEVICE_FLAG_INT_RX 或者 RT_DEVICE_FLAG_DMA_RX 的方式打开设备,否则不会回调函数。

应用程序打开设备完成读写等操作后,如果不需要再对设备进行操作则可以关闭设备,通过如下函数完成:

rt_err_t rt_device_close(rt_device_t dev);
参数 描述
dev 设备句柄
返回 ——
RT_EOK 关闭设备成功
-RT_ERROR 设备已经完全关闭,不能重复关闭设备
其他错误码 关闭设备失败

注意事项:

关闭设备接口和打开设备接口需配对使用,打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。

5.4、控制设备

通过命令控制字,应用程序也可以对设备进行控制,通过如下函数完成:

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
参数 描述
dev 设备句柄
cmd 命令控制字,这个参数通常与设备驱动程序相关
arg 控制的参数
返回 ——
RT_EOK 函数执行成功
-RT_ENOSYS 执行失败,dev 为空
其他错误码 执行失败

参数 cmd 的通用设备命令可取如下宏定义:

#define RT_DEVICE_CTRL_RESUME           0x01   /* 恢复设备 */
#define RT_DEVICE_CTRL_SUSPEND          0x02   /* 挂起设备 */
#define RT_DEVICE_CTRL_CONFIG           0x03   /* 配置设备 */
#define RT_DEVICE_CTRL_SET_INT          0x10   /* 设置中断 */
#define RT_DEVICE_CTRL_CLR_INT          0x11   /* 清中断 */
#define RT_DEVICE_CTRL_GET_INT          0x12   /* 获取中断状态 */

5.5、读写设备

  • 应用程序从设备中读取数据可以通过如下函数完成:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);
参数 描述
dev 设备句柄
pos 读取数据偏移量
buffer 内存缓冲区指针,读取的数据将会被保存在缓冲区中
size 读取数据的大小
返回 ——
读到数据的实际大小 如果是字符设备,返回大小以字节为单位,如果是块设备,返回的大小以块为单位
0 需要读取当前线程的 errno 来判断错误状态

调用这个函数,会从 dev 设备中读取数据,并存放在 buffer 缓冲区中,这个缓冲区的最大长度是 size,pos 根据不同的设备类别有不同的意义。

  • 向设备中写入数据,可以通过如下函数完成:
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);
参数 描述
dev 设备句柄
pos 写入数据偏移量
buffer 内存缓冲区指针,放置要写入的数据
size 写入数据的大小
返回 ——
写入数据的实际大小 如果是字符设备,返回大小以字节为单位;如果是块设备,返回的大小以块为单位
0 需要读取当前线程的 errno 来判断错误状态

调用这个函数,会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的最大长度是 size,pos 根据不同的设备类别存在不同的意义。

5.6、数据收发回调

当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达:

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
参数 描述
dev 设备句柄
rx_ind 回调函数指针
返回 ——
RT_EOK 设置成功

该函数的回调函数由调用者提供。当硬件设备接收到数据时,会回调这个函数并把收到的数据长度放在 size 参数中传递给上层应用。上层应用线程应在收到指示后,立刻从设备中读取数据。

在应用程序调用 rt_device_write() 入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后 (例如 DMA 传送完成或 FIFO 已经写入完毕产生完成中断时) 调用。可以通过如下函数设置设备发送完成指示,函数参数及返回值见:

rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
参数 描述
dev 设备句柄
tx_done 回调函数指针
返回 ——
RT_EOK 设置成功

Quando esta função é chamada, a função de retorno de chamada é fornecida pelo chamador.Quando o dispositivo de hardware terminar de enviar os dados, o driver irá chamar de volta esta função e passar o buffer de endereço do bloco de dados que foi enviado para a aplicação superior como parâmetro. O aplicativo da camada superior (thread) irá liberar o bloco de memória do buffer ou usá-lo como buffer para os próximos dados de gravação de acordo com a situação de envio do buffer ao receber a instrução.

5.7. Exemplo de acesso ao dispositivo

O código de amostra a seguir para o programa que acessa o dispositivo, primeiro rt_device_find()olha para o dispositivo de porta de watchdog, para obter um identificador para o dispositivo e, em seguida, através do rt_device_init()aparelho de inicialização de porta, por meio rt_device_control()do tempo de abertura de estouro do dispositivo de watchdog.

#include <rtthread.h>
#include <rtdevice.h>

#define IWDG_DEVICE_NAME    "iwg"

static rt_device_t wdg_dev;

static void idle_hook(void)
{
    
    
    /* 在空闲线程的回调函数里喂狗 */
    rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
    rt_kprintf("feed the dog!\n ");
}

int main(void)
{
    
    
    rt_err_t res = RT_EOK;
    rt_uint32_t timeout = 1000;    /* 溢出时间 */

    /* 根据设备名称查找看门狗设备,获取设备句柄 */
    wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
    if (!wdg_dev)
    {
    
    
        rt_kprintf("find %s failed!\n", IWDG_DEVICE_NAME);
        return RT_ERROR;
    }
    /* 初始化设备 */
    res = rt_device_init(wdg_dev);
    if (res != RT_EOK)
    {
    
    
        rt_kprintf("initialize %s failed!\n", IWDG_DEVICE_NAME);
        return res;
    }
    /* 设置看门狗溢出时间 */
    res = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
    if (res != RT_EOK)
    {
    
    
        rt_kprintf("set %s timeout failed!\n", IWDG_DEVICE_NAME);
        return res;
    }
    /* 设置空闲线程回调函数 */
    rt_thread_idle_sethook(idle_hook);

    return res;
}

5.8. Processo de interação entre o espaço do usuário melis e o driver do kernel - ioctl

Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/houxiaoni01/article/details/108343772
Recomendado
Clasificación