Introdução ao Desenvolvimento de Arquitetura de Software BES SDK

Olá pessoal! Da última vez, escrevi um módulo de comunicação serial de fio único baseado no módulo Hengxuan. Este artigo fará uma breve introdução ao Hengxuan Bluetooth SDK.

1. Arquitetura do sistema de software

1. O BES usa RXT RTOS (sistema operacional embutido em tempo real) e usa a interface CMSIS_RTOS API da ARM

2. Sabemos que o lugar onde o programa começa é _main_init () em RTX_CM_LIB.H, que é principalmente para inicializar o kernel, definir a pilha, criar a thread principal e iniciar o kernel.

3. O primeiro thread os_thread_def_main é main e, em seguida, examine-o no arquivo main.cpp.

Em seguida, é a inicialização da porta de depuração (usamos debug = 1 para um lado) por padrão para habilitar a saída de impressão do UART0 RX.

Depois que o uart for inicializado, ele imprimirá o software atual, chip, tamanho da partição flash e outras informações em hal_trace.c:

A impressão é aproximadamente a seguinte:

[18:35:14.391]CHIP=best1305
[18:35:14.391]KERNEL=RTX5
[18:35:14.391]CRASH_DUMP_SIZE=0
[18:35:14.391]AUD_SEC_SIZE=0x10000 [
18: 35: 14.391ovit
[18:35:14.391]FACT_SEC_SIZE=0x1000
[18:35:14.391]NV_REC_DEV_VER=2
[18:35:14.391]FLASH_BASE=0x38000000
[18:35:14.391]FLASH_SIZE=0x400000
[ 18: 35: 14.391x ] OTA_CODE28
[18:35:14.391]CRC32_OF_IMAGE=0x00000000
[18:35:14.391]BUILD_DATE=Mar 10 2021 18:21:32
[18:35:14.391]REV_INFO=:best2500i_JBL_T230
[18: 35: 14.391]
[18:35: 14.391]
[18:35:14.391]------
[18: 35: 14.391] METAL_ID: 0
[18:35:14.391]------

Em seguida, o software será executado para determinar se o tamanho real do flash corresponde à definição do software. Se o tamanho do flash definido pelo software exceder o tamanho do flash embutido do chip, ele será impresso

“FLASH_SIZE errado definido em target.mk!”

“FLASH_SIZE é definido como 0x20000 enquanto o tamanho real do flash do chip é 0x40000” 随后 死机。

Se não houver nenhuma anormalidade no flash, a configuração IO do hardware e a configuração IO de entrada e saída analógica serão iniciadas.

Então correu para app_init

Finalmente, se a inicialização for concluída, a tarefa do thread será executada em um loop infinito

 

Se a inicialização em app_init terminar prematuramente e retornar diferente de zero, desligue diretamente

 

4. Criação e uso de thread / mensagem de tarefa / cronômetro do BES.

Deixe-me falar sobre um entendimento conceitual primeiro: 1. Thread é um processo de agendamento de tarefas baseado em RTOS (simulação de intervalo de tempo de ocupação multi-core)

                                  2. Cada thread é independente e não será executada ao mesmo tempo 

                                  3. Um encadeamento pode conter processamento de vários módulos de mensagem.

A. Tópico

O primeiro thread os_thread_def_main é main, então olhe para ele, no arquivo main.cpp:

Há criação de thread em app_os_init (). Este é o estilo do CMSIS. Se você desenvolveu STM32, deve estar familiarizado com ele. Ele é usado para definir threads, temporizadores e comunicações de caixa de correio, etc .;
app_thread_tid = osThreadCreate (osThread ( app_thread), NULL);

app_thread é a função de loop de thread, OSThread é na verdade uma macro, apenas para pegar o endereço
#define osThread (name) & os_thread_def _ ## name

Portanto, OSThreadDef deve definir o nome da thread, a prioridade e o tamanho da pilha, obter o ponteiro da variável de estrutura configurada por meio de osThread e, em seguida, passá-lo para OSThreadCreate () como um parâmetro.

Geralmente, você verá esta definição no início do arquivo: osThreadDef é na verdade uma macro
osThreadDef (app_thread, osPriorityHigh, 1, 1024, “app_thread”);

PS: A definição de thread inicial é assim:

/ * thread * /
static osThreadId uartrxtx_tid; 
uint32_t os_thread_def_stack_uartrxtx [UART_STACK_SIZE / sizeof (uint32_t)];
osThreadDef_t os_thread_def_app_uartrxtx = {(app_uartrxtx), (osPriorityHigh), (UART_STACK_SIZE), (os_thread_def_stack_uartrxtx)};

Aqui é inicializar e atribuir uma variável de estrutura, e então passar o endereço de os_thread_def_app_thread para os_thread (). O tipo de estrutura é:

/ ==== Funções de gerenciamento de tópicos ====

/// Crie uma Definição de Thread com requisitos de função, prioridade e pilha.
/// \ param name nome da função de thread.
/// \ param prioridade prioridade inicial da função thread.
/// \ param instâncias número de possíveis instâncias de thread.
/// \ param stacksz requisitos de tamanho de pilha (em bytes) para a função de thread.
/// \ note PODE SER ALTERADO: Os parâmetros para \ b osThreadDef devem ser consistentes, mas o
corpo da macro /// tem implementação específica em cada CMSIS-RTOS.

#define osThreadDef (nome, prioridade, instâncias, stacksz, task_name) \
uint64_t os_thread_def_stack _ ## name [(8 * ((stacksz + 7) / 8)) / sizeof (uint64_t)]; \
const osThreadDef_t os_thread_def _ ## name = \
{(nome), \
  {task_name, osThreadDetached, NULL, 0U, os_thread_def_stack _ ## name, 8 * ((stacksz + 7) / 8), (priority), 1U, 0U}}
O seguinte é sobre a criação de thread:

Vejamos a execução do thread:

Vamos dar uma olhada no que é feito no thread app_thread:
if (mod_handler [msg_p-> mod_id])
int ret = mod_handler [msg_p-> mod_id] (& (msg_p-> msg_body));

Podemos ver que no thread app_thread, as informações da caixa de correio são obtidas repetidamente por meio de app_mailbox_get () e passadas para mod_handler [].
static APP_MOD_HANDLER_T mod_handler [APP_MODUAL_NUM];
Insira a descrição da imagem aqui
Observe seu tipo de dados como uma matriz de ponteiros de função e observe a definição de seu subscrito de matriz:

A conexão de código real usada para a criação de thread é a seguinte (incluindo as versões antiga e nova):

https://share.weiyun.com/GUat9L7n

 

B: Tarefa de mensagem:

A tarefa da mensagem é baseada no próximo nível de processamento da discussão 

No momento, o processamento geral da tarefa de mensagem é baseado no thread app_thread.Claro, o processamento da tarefa também pode ser adicionado aos threads restantes.

A partir da explicação do thread acima, sabemos que cada módulo registrará sua própria função de retorno de chamada e, em seguida, app_thread () irá processá-la de acordo com a API correspondente ao módulo de retorno de chamada de mensagem get.

Portanto, a tarefa precisa registrar a função de execução da mensagem após app_os_init antes de registrar.

 

Interface geral da função de processamento de mensagens:

O valor de retorno são dados do tipo int, que carregam o ponteiro do parâmetro de estrutura de APP_MESSAGE_BOY *.

Aqui está uma explicação do processamento de mensagens NTC que escrevi:

Cronômetro C:

Os temporizadores são divididos em temporizadores de software e temporizadores de hardware, que essencialmente executam o processamento de interrupção quando o tempo se esgota.

A diferença é que o temporizador do hardware é baseado no relógio do hardware, por isso é relativamente mais preciso e a precisão da medição é menor, e o temporizador do hardware é único.

O cronômetro pode ser recarregado, mas não pode ser usado em interrupções .

C1: temporizador de hardware

Arquivo de cabeçalho: #include "hwtimer_list.h"

static HWTIMER_ID app_box_det_debounce_tid = NULL; // A definição de ID do temporizador de hardware é geralmente definida como todas as variáveis

app_box_det_debounce_tid = hwtimer_alloc (app_box_det_debounce_timehanlder, NULL); // Função de execução de interrupção do temporizador associada. app_box_det_debounce_timehanlder carregará parâmetros do tipo void *.

hwtimer_start (app_box_det_debounce_tid, MS_TO_TICKS (200)); // Para iniciar o cronômetro, observe que você não pode preencher o tempo diretamente depois, ele precisa ser convertido em um relógio

hwtimer_stop (app_box_det_debounce_tid); // O cronômetro pode ser interrompido mais cedo, remova a fila e altere o cronômetro, pois ele não é mais executado.

 

C2: Temporizador do software:

Arquivo head:

#include "cmsis_os.h"
#include "hal_timer.h"

typedef enum {   osTimerOnce = 0, /// <Temporizador de disparo único.   osTimerPeriodic = 1 /// <Temporizador de repetição. } osTimerType_t;


static osTimerId app_battery_timer = NULL; // ID do temporizador de software  
static void app_battery_timer_handler (void const * param); // Função de execução de interrupção do temporizador, pode carregar parâmetros de tipo void *
osTimerDef (APP_BATTERY, app_battery_timer_handler); // Temporizador de software de macro precisa ser usado temporizador associado e função de execução

app_battery_timer = osTimerCreate (osTimer (APP_BATTERY), osTimerPeriodic, NULL); // criação do cronômetro de software APP_BATTERY é o ID de endereçamento do cronômetro 

O segundo parâmetro é se o cronômetro é periódico ou não. Se for único, não será executado novamente após o término do tempo, mas o ID ainda pode ser iniciado novamente.

 osTimerStop (app_battery_timer); // Parar o cronômetro do software
 osTimerStart (app_battery_timer, 5000); // Reiniciar o cronômetro do software e executar a interrupção do cronômetro após 5S.

 

A arquitetura do sistema deste artigo está aqui por enquanto. A análise detalhada dos submódulos de acompanhamento. Continue a se inscrever para visualizar, obrigado!

QQ: 1902026113

 

 

Acho que você gosta

Origin blog.csdn.net/qq_34990604/article/details/114928487
Recomendado
Clasificación