1. 1. Introdução
Em pauta estava a tarefa de desenvolver um protocolo de comunicação para o microcontrolador nrf52832 com dois extensômetros chineses de meia ponte.
A tarefa acabou por não ser fácil, pois me deparei com a falta de informações inteligíveis. É mais provável que a “raiz do mal” esteja no SDK da própria Nordic Semiconductor – atualizações constantes de versão, alguma redundância e funcionalidades confusas. Tive que escrever tudo do zero.
Acho que este tópico é bastante relevante pelo fato de este chip possuir uma pilha BLE e todo um conjunto de “guloseimas” para o modo de economia de energia. Mas não vou me aprofundar muito na parte técnica, já que muitos artigos foram escritos sobre esse tema.
2. Descrição do projeto
Ferro:
- Adafruit Feather nRF52 Bluefruit LE (o que estava em mãos)
- ADC HX711
- Medidores de tensão chineses 2 unid. (50x2kg)
- Programador ST-LINK V2
Programas:
- IDE VSCODE
- SDK NRF 16
- OpenOCD
- Programador ST-LINK V2
Tudo está em um projeto, basta ajustar o Makefile (especificar a localização do seu SDK).
3. Descrição do código
Utilizaremos o módulo GPIOTE para trabalhar com periféricos baseado na vinculação de tarefas e eventos, bem como o módulo PPI para transferir dados de um periférico para outro sem a participação de um processador.
ret_code_t err_code;
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config);//настраеваем на выход
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);//будем передергивать пин для импульса
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config);//настраеваем на выход
Configuramos a linha de sincronização PD_SCL para a saída para gerar pulsos com duração de 10 μs.
nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false);// переход уровня с высокого на низкий
nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL);// на вход без подтяжки
err_code = nrf_drv_gpiote_in_init(DOUT, &gpiote_config, gpiote_evt_handler);
static void gpiote_evt_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_drv_gpiote_in_event_disable(DOUT);//отключаем прерывание
nrf_drv_timer_enable(&m_timer0);//включаем таймер
}
Configuramos a linha de dados DOUT para ler o estado de prontidão do HX711; se houver nível baixo, é acionado um manipulador no qual desabilitamos a interrupção e iniciamos um timer para gerar pulsos de clock na saída PD_SCL.
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1, nrf_drv_timer_event_address_get(&m_timer0, NRF_TIMER_EVENT_COMPARE0), nrf_drv_gpiote_out_task_addr_get(PD_SCK));// подключаем таймер к выходу
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);// включаем канал
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_task_enable(PD_SCK);
// habilita o gpiote
Em seguida, inicializamos o módulo PPI e conectamos nosso temporizador à saída PD_SCL para gerar pulsos com duração de 10 μs quando ocorrer um evento de comparação, e também ligamos o módulo GPIOTE.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;// по умолчанию
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz;// тактируем на частоте 1Мгц
ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_timer_extended_compare(&m_timer0,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_us_to_ticks(&m_timer0,
10),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);// срабатывает по сравнению
Inicializamos o temporizador zero e seu manipulador.
if(m_counter%2 != 0 && m_counter<=48){
buffer <<= 1;// переменная считанных даных
c_counter++;// счетчик положительных импульсов
if(nrf_gpio_pin_read(DOUT))buffer++;//считываем состояние входа
}
O mais interessante acontece no manipulador do timer. O período de pulso é de 20 μs. Estamos interessados em pulsos ímpares (ao longo da borda ascendente) e desde que seu número não seja superior a 24 e haja 48 eventos. Para cada evento ímpar, DOUT é lido
Segue-se da ficha técnica que o número de pulsos deve ser no mínimo 25, o que corresponde a um ganho de 128 (no código usei 25 pulsos), isso equivale a 50 eventos temporizadores, o que indica o fim do quadro de dados.
++m_counter;// счетчик событий
if(m_counter==50){
nrf_drv_timer_disable(&m_timer0);// отключаем таймер
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED;//
buffer = buffer ^ 0x800000;
hx711_stop();//jотключаем hx711
}
Depois disso, desligamos o timer e processamos os dados (conforme ficha técnica) e colocamos o HX711 em modo de baixo consumo de energia.
static void repeated_timer_handler(void * p_context)
{
nrf_drv_gpiote_out_toggle(LED_2);
if(m_simple_timer_state == SIMPLE_TIMER_STATE_STOPPED){
hx711_start();// включаем hx711
nrf_drv_gpiote_out_toggle(LED_1);
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
}
}
/**@brief Create timers.
*/
static void create_timers()
{
ret_code_t err_code;
// Create timers
err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
Esperamos eventos do temporizador RTC com intervalo de 10 s (a seu critério) e lançamos o HX711 no manipulador, causando uma interrupção na linha DOUT.
Há mais um ponto, os logs são gerados via UART (taxa de transmissão 115200, TX - 6 pinos, RX - 8 pinos) todas as configurações estão em sdk_config.h
Descobertas
Obrigado a todos pela atenção, espero que este artigo seja útil e reduza o tempo valioso para os desenvolvedores encontrarem uma solução. Quero dizer que a abordagem técnica que a Nordic utiliza nas suas plataformas é bastante interessante do ponto de vista da eficiência energética.
PS
O projeto ainda está em desenvolvimento, então se este tópico for de interesse, no próximo artigo tentarei descrever o algoritmo para calibração de sensores de peso, bem como conexão da pilha BLE.
materiais
Fonte: habr.com