Gravação RT-Thread (quatro, batida de relógio RT-Thread e temporizador de software)

RT-Thread第4课,听听 RT-Thread 的心跳,再学习一下基于心跳的软件定时器使用。

prefácio

Aprendendo RTOS, você deve estar exposto a temporizadores de software, e aprender o uso de temporizadores de software pode nos livrar das limitações de temporizadores de hardware em alguns lugares, e a realização de temporizadores de software é baseada na batida do relógio do sistema. Além de entender a API do temporizador do software RT-Thread, para aprender a usar o temporizador do software RT-Thread, você também precisa entender o conhecimento relacionado ao gerenciamento do relógio RT-Thread.

1. Batida do relógio RT-Thread

1.1 O conceito de batidas do relógio

O tique-taque do relógio (OS Tick) é a pulsação do sistema! Qualquer sistema operacional precisa fornecer um clock para o sistema processar todos os eventos relacionados ao tempo.

A menor unidade de tempo no sistema operacional é o tique-taque do relógio, que é uma interrupção periódica específica, e o kernel realiza a troca de contexto quando o tique-taque do relógio chega.

No RT-Thread, a duração da batida do relógio pode ser ajustada de acordo com RT_TICK_PER_SECONDa definição de , que é igual a 1/RT_TICK_PER_SECONDsegundos. No STM32F que testamos, a batida do relógio padrão é 1ms, como segue:
insira a descrição da imagem aqui
O último parâmetro ao criar uma thread em a seção anterior é o número de batidas de tempo , por exemplo, se estiver definido como 50, a fatia de tempo do encadeamento será de 50ms.

Além disso, rtconfig.hexistem configurações de definição de macro para configuração de kernel RT-Thread, configuração de comunicação de thread, configuração de componente, configuração de shell, configuração de driver de dispositivo e assim por diante.

RT_TICK_PER_SECONDPode ser modificado, por exemplo, modificamos para 100. O tique-taque do relógio é de 10ms.

1.2 O princípio da implementação da batida do relógio

Então, como a batida do tempo é alcançada?
Como mencionado anteriormente: a batida do relógio é uma interrupção periódica específica. Esta interrupção é geralmente determinada pelo timer de hardware do MCU, se a batida do relógio do sistema é gerada ou baseada no timer de hardware do MCU!

Para o Corex-Mchip , a batida do relógio do sistema é realizada pelo cronômetro de tique-taque Systick.

Por estar relacionado ao timer de ticks Systick, podemos dar uma olhada no código de engenharia, como mostra a figura:
insira a descrição da imagem aqui
No manipulador de interrupção do timer de ticks, podemos ver as seguintes operações: o valor
insira a descrição da imagem aqui
da variável global no código acima rt_tickIndica o número total de tiques de relógio decorridos desde que o sistema foi iniciado, ou seja, a hora do sistema. rt_tick é incrementado em 1 cada vez que um tique do relógio passa.

Aqui está outra pergunta, o temporizador de tiques Systick não é fixo no STM32?
Então aqui vamos falar sobre o que RT_TICK_PER_SECONDmudou e como essa definição macro afeta a batida do sistema.

Encontramos drv_common.ca função no arquivo rt_hw_systick_init, conforme mostrado na figura abaixo:
insira a descrição da imagem aqui
A figura acima é a função que o RT-Thread inicializa e configura para iniciar o timer de ticks do MCU RT_TICK_PER_SECOND. pode alterar diretamente a frequência do Systick, fazendo com que as batidas do relógio dos sistemas sejam diferentes.

1.3 Exemplo de tique-taque do relógio

Como mencionamos acima, a variável global rt_tickrepresenta o número total de tiques do relógio que passaram desde que o sistema foi iniciado. RT-Thread nos fornece uma função rt_tick_getpara visualizar o valor atual do tique do relógio:

/*
返回值:rt_tick 	当前时钟节拍值
*/
rt_tick_t rt_tick_get(void);

Para consolidar o conteúdo acima, vamos fazer um teste simples, pois o teste é relativamente simples, vou direto para a imagem:
insira a descrição da imagem aqui
quando RT_TICK_PER_SECONDfor 1000, significa que definimos o batimento do sistema para 1ms, então o valor do tick é 1ms mais uma vez, então após o atraso de 1000ms, ele é aumentado em 1000.
Quando RT_TICK_PER_SECONDé 100, significa que configuramos o batimento do sistema para 10ms, então o valor do tick é 10ms mais uma vez, então após o atraso de 1000ms, é aumentado em 100.

2. Temporizador de software RT-Thread

2.1 Introdução básica do temporizador de software

O sistema operacional RT-Thread fornece um timer implementado por software, que toma como unidade a duração do clock tick (OS Tick), ou seja, o valor de tempo deve ser um múltiplo inteiro do OS Tick.

Os temporizadores são divididos em temporizadores de disparo único e temporizadores de disparo periódico;

O cronômetro é relativamente simples de usar e entender. Para algumas precauções de temporização do software, escrevi-o quando apresentei o cronômetro do software FreeRTOS. Você pode compará-lo e consultá-lo da seguinte forma:
insira a descrição da imagem aqui
Ao contrário do FreeRTOS, ele é executado de acordo com a função de tempo limite Dependendo do contexto em que está localizado, os temporizadores RT-Thread podem ser divididos em modo HARD_TIMER e modo SOFT_TIMER:

  • Modo HARD_TIMER

A função timer timeout no modo HARD_TIMER é executada no contexto de interrupção.O modo padrão do timer RT-Thread é o modo HARD_TIMER, ou seja, após o timer expirar, a função timeout é executada no contexto da interrupção do relógio do sistema.

Simplificando, a função de retorno de chamada no modo HARD_TIMER deve ser tratada como uma função de interrupção, rápida e rápida.

  • Modo SOFT_TIMER

O modo SOFT_TIMER é configurável e RT_USING_TIMER_SOFTé se esse modo deve ser ativado. Após este modo ser habilitado, o sistema criará uma thread de timer durante a inicialização e então a função timer timeout do modo SOFT_TIMER será executada no contexto da thread de timer. RT_TIMER_FLAG_SOFT_TIMERO modo SOFT_TIMER pode ser especificado com um parâmetro ao inicializar/criar o timer .

O hábito pessoal é que o aplicativo ainda está definido RT_USING_TIMER_SOFT, e então o modo SOFT_TIMER é usado.Pessoalmente, sinto que isso é mais "como" um timer de software.

Por fim, gostaria de dar uma sugestão: em aplicações práticas, seja no modo HARD_TIMER ou no modo SOFT_TIMER, na função timeout, é necessário fazer o fast-forward e fast-out sem atrasar e suspender as operações.

2.2 Inicialização do temporizador do sistema

No uso do RT-Thread, muitas vezes é definido RT_USING_TIMER_SOFTusar o timer do software e iniciar o modo SOFT_TIMER. Após esse modo ser ativado, o sistema criará um timerthread para gerenciar frequentemente o timer do software. Em seguida, usamos o código-fonte para ver como o RT-Thread funciona.

Através do estudo de "RT-Thread Record (2. RT-Thread Kernel Startup Process - Startup File and Source Code Analysis)", podemos encontrar a rtthread_startupfunção:
insira a descrição da imagem aqui
vamos dar uma olhada na primeira rt_system_timer_init:
insira a descrição da imagem aqui

Em seguida, olhe para a segunda função rt_system_timer_thread_init:

insira a descrição da imagem aqui
Vamos continuar timerentrando na função entry do thread para ver o que timero thread faz especificamente, aqui vamos analisá-lo colocando o código fonte e olhando os comentários:

/* system timer thread entry */
static void rt_thread_timer_entry(void *parameter)
{
    
    
    rt_tick_t next_timeout;

    while (1)
    {
    
    
        /* 
        get the next timeout tick
        获取下一次超时时间
        得到软件定时器链表上的下一个定时器的超时时间点   
		*/
        next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list);
        /*
        如果超过范围,表示没有软件定时器,
        则挂起当前线程,继续线程调度
        */
        if (next_timeout == RT_TICK_MAX)
        {
    
    
            /* no software timer exist, suspend self. */
            rt_thread_suspend(rt_thread_self());
            rt_schedule();
        }
        else
        {
    
    
            rt_tick_t current_tick;

            /* 
            get current tick 
            获取当前时间点
            */
            current_tick = rt_tick_get();
			 /*
			 离下个中断时间点还差些时候
			 */
            if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
            {
    
    
                /* get the delta timeout tick */
                next_timeout = next_timeout - current_tick;//计算还差多长时间
                rt_thread_delay(next_timeout);//休眠差的这段时间 
            }
        }

        /* 
        check software timer
        检查是否该产生超时事件
        */
        rt_soft_timer_check();
    }
}
#endif

Se você quiser continuar a análise abaixo, você deve continuar analisando rt_soft_timer_check();o código-fonte da implementação. Não continuaremos a analisá-lo aqui, porque até agora temos um entendimento abrangente do processo de inicialização do timer do sistema RT-Thread. Entender os cronômetros é muito útil, mas amigos que gostam de estudar podem continuar analisando o seguinte: A análise do código-fonte é a maneira mais direta e eficaz de entender um sistema!

2.3 Mecanismo de trabalho do temporizador

Assim como o bloco de controle de thread, o kernel gerencia o timer através desta estrutura de bloco de controle de timer, que inclui todos os "atributos" do timer de software RT-Thread. A visualização e modificação desses atributos podem ser usados ​​para obter esse controle de gerenciamento de timers de software.

/**
 * timer structure
 */
struct rt_timer
{
    
    
	/**< inherit from rt_object */
    struct rt_object parent;                            
	/* 定时器链表节点 */
    rt_list_t        row[RT_TIMER_SKIP_LIST_LEVEL];    
	/**< timeout function 定时器超时调用的函数 */
    void (*timeout_func)(void *parameter);
    /**< timeout function's parameter 超时函数的参数*/             
    void            *parameter;                         
 	/**		 < timer timeout tick  定时器初始超时节拍数 */
    rt_tick_t        init_tick;
    /**< timeout tick 定时器实际超时时的节拍数*/                         
    rt_tick_t        timeout_tick;                     
};
typedef struct rt_timer *rt_timer_t;

O bloco de controle do timer é definido pela estrutura struct rt_timer e forma o objeto do kernel do timer, que é então vinculado ao contêiner do objeto do kernel para gerenciamento. O membro da lista é usado para vincular um timer ativo (já iniciado) à lista vinculada rt_timer_list.

Para o mecanismo de trabalho do timer, a introdução do RT-Thread foi muito detalhada. Aqui eu uso uma imagem oficial para mostrá-lo:
insira a descrição da imagem aqui
Para o mecanismo de trabalho do timer, não nos ajuda diretamente a usar o timer (porque o temporização O uso do cronômetro é muito simples), mas pode nos dar uma compreensão mais profunda do cronômetro. Muitas vezes, esses entendimentos mais profundos desempenham um papel crucial na resolução de problemas quando os encontramos.

2.4 us função de atraso

Qualquer pessoa que tenha usado a biblioteca STM32 HAL sabe que a biblioteca HAL não possui atraso de nós e, no FreeRTOS, não há função de atraso de nós. Mas quando fazemos algumas operações de barramento, como comunicação I2C por software, temos que usar a função us delay.
Bem, agora, ao usar o RT-Thread, o sistema nos fornece diretamente uma função de atraso, como segue:

/**
 * This function will delay for some us.
 *
 * @param us the delay time of us
 */
void rt_hw_us_delay(rt_uint32_t us)
{
    
    
    rt_uint32_t start, now, delta, reload, us_tick;
    start = SysTick->VAL;
    reload = SysTick->LOAD;
    /* 获得延时经过的 tick 数 */
    us_tick = SystemCoreClock / 1000000UL;
    do {
    
      	
        now = SysTick->VAL; // 获得当前时间 
        delta = start > now ? start - now : reload + start - now;
    } while(delta < us_tick * us);
}

Observe que esta função só pode suportar atrasos abaixo de 1 OS Tick. Por exemplo, nossa configuração padrão, 1OS Tick é 1ms, então o parâmetro us desta função deve ser menor que 1000!

2.5 Temporizador de software ou temporizador de hardware?

Então, em aplicações práticas, é usado um temporizador de software ou um temporizador de hardware? Eu expliquei isso em outro post do blog, como mostrado na figura abaixo:
insira a descrição da imagem aqui
Usando os chips da série STM32 que normalmente usamos como exemplo, também precisamos ver qual série de chips. toda a série M0 ou M3M4. Em toda a série, há pelo menos 4 (por favor, indique se houver erros), se houver temporizadores de hardware suficientes, você não pode usar o temporizador de software. Para algumas séries de sinais, a RAM é relativamente pequeno, e alguns têm menos de 10 KB, então você Ao executar o RTOS, você deve prestar atenção especial ao tamanho da memória. Se você não sabe muito sobre gerenciamento de memória e otimização de código neste momento, eu não recomendo isto.
No entanto, em algumas aplicações especiais, os temporizadores de software têm vantagens óbvias sobre os temporizadores de hardware, porque o tempo de atraso pode ser alterado à vontade. O código que também usa temporizadores de software é mais rápido para portar.

3. Função de operação do temporizador do software RT-Thread

Há muito conhecimento básico acima, e é hora de falar sobre como usá-lo.
(Apresse-se! Apresse-se! A teoria está adormecendo! Use-a! Mostre!)

3.1 Criar e excluir temporizadores dinamicamente

Crie dinamicamente um temporizador (veja os comentários para a introdução da função, as seguintes introduções de função são semelhantes):

/*
参数的含义:
1、name 	定时器的名称
2、void (timeout) (void parameter) 	定时器超时函数指针(当定时器超时时,系统会调用这个函数)
3、parameter 	定时器超时函数的入口参数(当定时器超时时,调用超时回调函数会把这个参数做为入口参数传递给超时函数)
4、time 	定时器的超时时间,单位是时钟节拍
5、flag 	定时器创建时的参数,支持的值包括单次定时、周期定时、硬件定时器、软件定时器等(可以用 “或” 关系取多个值)
返回值:
RT_NULL   	创建失败(通常会由于系统内存不够用而返回 RT_NULL)
定时器的句柄 	定时器创建成功
 */
rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag)

A função acima, o quinto parâmetro flag, possui 2 conjuntos de valores que podem ser preenchidos (o mesmo vale para timers criados estaticamente), conforme mostrado abaixo:
insira a descrição da imagem aqui

Os 2 conjuntos de definições de macro acima podem ser atribuídos de maneira lógica "ou" flag, por exemplo:
insira a descrição da imagem aqui

Temporizador de exclusão dinâmica :

/*
参数:
timer 	定时器句柄,指向要删除的定时器控制块
返回值:
RT_EOK 	删除成功
*/
rt_err_t rt_timer_delete(rt_timer_t timer);

3.2 Criar e excluir temporizadores estaticamente

Assim como os tópicos, os termos oficiais são timers de inicialização e desconexão (o motivo foi analisado no meu último post no blog).

Crie um temporizador estaticamente :

/*
参数的含义:
1、timer 	定时器句柄,指向要初始化的定时器控制块,用户创建的定时器控制块结构体
2、name 	定时器的名称
3、void (timeout) (void parameter) 	定时器超时函数指针(当定时器超时时,系统会调用这个函数)
4、parameter 	定时器超时函数的入口参数(当定时器超时时,调用超时回调函数会把这个参数做为入口参数传递给超时函数)
5、time 	定时器的超时时间,单位是时钟节拍
6、flag 	定时器创建时的参数,支持的值包括单次定时、周期定时、硬件定时器、软件定时器等(可以用 “或” 关系取多个值)
返回值:
无返回值
 */
void rt_timer_init(rt_timer_t  timer,
                   const char *name,
                   void (*timeout)(void *parameter),
                   void       *parameter,
                   rt_tick_t   time,
                   rt_uint8_t  flag)

Temporizador de exclusão estática :

/**
参数:
timer 	定时器句柄,指向要删除的定时器控制块
返回值:
RT_EOK 	删除成功
 */
rt_err_t rt_timer_detach(rt_timer_t timer)

3.3 Iniciando e Parando o Temporizador

Inicie o temporizador :

Quando o timer é criado ou inicializado, ele não será iniciado imediatamente. Ele deve começar a funcionar após chamar a interface da função start timer. A interface da função start timer é a seguinte:

/**
参数:
timer 	定时器句柄,指向要启动的定时器控制块
返回值:
RT_EOK 	启动成功
 */
rt_err_t rt_timer_start(rt_timer_t timer)

Pare o temporizador :

/**
参数:
timer 	定时器句柄,指向要启动的定时器控制块
返回值:
RT_EOK 		成功停止定时器
- RT_ERROR 	timer 已经处于停止状态
 */
rt_err_t rt_timer_stop(rt_timer_t timer)

Depois de chamar a interface da função de parada do temporizador, o estado do temporizador será alterado para o estado de parada e será separado da lista vinculada rt_timer_list e não participará da verificação de tempo limite do temporizador.

Quando um temporizador periódico expira, você também pode chamar essa interface de função para interromper o próprio temporizador.

3.4 Função de Controle do Temporizador

O RT-Thread fornece uma interface de função de controle do temporizador para obter ou definir mais informações do temporizador.

/**
参数的含义:
1、timer 	 	定时器句柄,指向要控制的定时器控制块
2、cmd 	
用于控制定时器的命令,当前支持5个命令,
分别是设置定时时间,查看定时时间,设置单次触发,设置周期触发,查看状态
3、arg 	
与 cmd 相对应的控制命令参数 
比如,cmd 为设定超时时间时,就可以将超时时间参数通过 arg 进行设定
返回值:
RT_EOK 	成功
 */
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)

Segundo o site oficial, o segundo parâmetro da função acima possui apenas 4 comandos, mas na verdade, descobri que a versão atual já possui 5 comandos através do meu próprio projeto, como segue:
insira a descrição da imagem aqui

Quarto, o exemplo de uso do temporizador

O uso de temporizadores é relativamente simples, e explicaremos os exemplos a seguir diretamente por meio de capturas de tela.

4.1 Exemplo de criação dinâmica de um temporizador

insira a descrição da imagem aqui

4.2 Exemplo de temporizador criado estaticamente

insira a descrição da imagem aqui

Epílogo

O conteúdo deste artigo na verdade é relativamente simples. No exemplo de uso, mostramos apenas o método de criação e o efeito de uso do timer. A referência real tem conseguido atender às necessidades da maioria das ocasiões. Os amigos podem criar um novo thread e tentar controlar um timer através da função de controle do timer, para ficar mais familiarizado com o uso do timer do software RT-Thread.

Ainda espero que amigos que entendam possam me dar alguns conselhos depois de lê-lo! Amigos que não entendem podem entender depois de ler! (Se você ainda não entendeu depois de ler, significa que o blogueiro não falou direito. Esse é o problema. O blogueiro tem que mudar! = =!)
Obrigado!

Acho que você gosta

Origin blog.csdn.net/weixin_42328389/article/details/123504395
Recomendado
Clasificación