Question about Bluepill arduino using OnePulse mode

Post here first, or if you can't find a relevant section!
xlwww
Posts: 14
Joined: Tue Dec 27, 2022 9:19 am

Re: Question about Bluepill arduino using OnePulse mode

Post by xlwww »

ozcar wrote: Mon Jan 02, 2023 10:58 am I can easily get your previous code to fail - as in stop responding altogether, by just presenting it with more pulses than it expects.

I cannot get it to stop responding if I add this to the handler_channel_1() routine:

Code: Select all

void handler_channel_1(void) {                           //This function is called when channel 1 is captured.
      if ( index1 > 49 ) return;                         // prevent overrun of data array
      if (0b1 & GPIOA_BASE->IDR  >> 0) {                 //If the receiver channel 1 input pulse on A0 is high.
      ... 
I'm not sure exactly what you have done now, but adding stray arrays is not the answer. If you do that, don't be surprised if it starts to fail again as soon as you add something else to it.

I can see that at some time you tried to print the index1 value in the interrupt routine, but that statement has been commented out. So, maybe you found out for yourself that using Serialx.print() is not such a good idea in an interrupt routine, but it seems that you were somehow suspicious of index1.

If you really want to work out what is happening, change the interrupt routine so that it does not just bail out completely when the index gets too great. Instead, when the index is greater than the array size, stop storing the values into the array, but still increment index1. Then print out index1 in loop(). I'll bet you will find that without that change you were running beyond the end of the array (in other words, you will sometimes see values of index1 > 49).
Yes I understand and corrected it

Code: Select all

void handler_channel_1(void) {                           //This function is called when channel 1 is captured.
    if (0b1 & GPIOA_BASE->IDR  >> 0) {                     //If the receiver channel 1 input pulse on A0 is high.
      channel_1_start = TIMER2_BASE->CCR1;                 //Record the start time of the pulse.
      TIMER2_BASE->CCER |= TIMER_CCER_CC1P;                //Change the input capture mode to the falling edge of the pulse.
    }
    else {                                                 //If the receiver channel 1 input pulse on A0 is low.
      channel_1 = TIMER2_BASE->CCR1 - channel_1_start;     //Calculate the total pulse time.
      if (channel_1 < 0)channel_1 += 0xFFFF;               //If the timer has rolled over a correction is needed.
      TIMER2_BASE->CCER &= ~TIMER_CCER_CC1P;               //Change the input capture mode to the rising edge of the pulse.
      DHT11_data[index1++]=channel_1;
      if(index1==41)
        Timer2.detachInterrupt(TIMER_CH1);
    }
}
ag123
Posts: 1668
Joined: Thu Dec 19, 2019 5:30 am
Answers: 25

Re: Question about Bluepill arduino using OnePulse mode

Post by ag123 »

There is another funky, hardware driven way using a counter is to use a timer and DMA.
Accordingly, a timer can trigger DMA, e.g. to read a set of gpios in sequence, this is actually 'the' way to 'read/write parallel data'.
As DHT11/DHT22 is first triggered using a single pulse from the mcu, this can be done using the usual digitalWrite().
The timer and dma needs to be setup in advance
Once done, enable dma and I think it'd begin reading (sampling) data in sync with the timer.
After this, you can 'parse' that data buffer to decode data sent from the DHT11/DHT22, the downside is this needs some memory.
The benefit is I think this approach may tolerate an RTOS 1ms context switching, as it is all hardware driven.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Question about Bluepill arduino using OnePulse mode

Post by dannyf »

which did not use a 'OnePulse' mode
seemed excessive to use a dedicated hardware peripheral for something like this: the device is essentially expecting a low pulse. you can use a uart to simulate that as well -> might be simpler than a timer.
The main thing about DHT11, DHT22 is measuring the time interval for '0' (zero) and '1' (one)
two things.
1. that chart is wrong as the master (mcu) pulling down the bus at end of the start signal. it should be pulled down by the slave (the device).
2. in reading the device, what matters is just the duration of the positive pulse from the slave. so something like this should work.

Code: Select all

//return the bus reading (1 bit)
char dhtRead1(PIN_TypeDef pin) {
  uint32_t tmp;
  while (digitalRead(pin)==LOW) continue;  //wait for the rising edge
  tmp = ticks(); //timestamp the rising edge
  while (gitialRead(pin)==HIGH) continue;  //wait for the falling edge
  return (ticks() - tmp < DHT_BIT1)?1:0;
}
DHT_BIT1 should be the tick counts that give you approximately 50us (0=28us, 1=70us, so 50us is somewhere in between).

building upon that, reading a byte would be something like this:

Code: Select all

//read a byte off the bus, msb first
char dhtRead8(PIN_TypeDef pin) {
	char tmp=0;
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	tmp = (tmp << 1) | dhtRead1(pin);
	return tmp;	
similarly, reading a dht can be built on that:

Code: Select all

//read dht: RHI, RHD, TI, TD, Checksum
void dhtRead(DHT_TypeDef *dht) {
  dht->RHI = dhtRead8(dht->pin);	//read rh interger part
  dht->RHD = dhtRead8(dht->pin);	//read rh decimal
  dht->TI   = dhtRead8(dht->pin);        //read temperature integer part
  dht->TD  = dhtRead8(dht->pin);        //read temperature decimal part
  dht->chksum=dhtRead8(dht->pin);   //read checksum
}

the drawback is that it is a blocking implementation.

for a non-blocking implementation, you can use external interrupts, or pin change interrupts, or even input capture. the downside is that the ISR overhead will mean that it is very difficult to implement somethng like thing on a slow mcu.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Question about Bluepill arduino using OnePulse mode

Post by dannyf »

Now given there was no DHT11 attached
you can write a dht slave to simulate a dht device, transmitting whatever data you want. making debugging easier. it is the opposite of what the OP wants to do but equally fun :)
ag123
Posts: 1668
Joined: Thu Dec 19, 2019 5:30 am
Answers: 25

Re: Question about Bluepill arduino using OnePulse mode

Post by ag123 »

@dannyf nice! I'd think counting 'ticks' should work but that it'd need to be micros(), perhaps micros() may be appropriate, but one'd need to be careful of wrap around.
And yes it is a 'blocking' implementation, in the sense that that reading shouldn't be interrupted as it would throw the measurements out.
like mentioned prior, I think among the alternatives, using timer and dma in conjunction may help, or perhaps a uart or spi can be used.
But for 'uart' I'd guess it is a little 'tricky' there, but if it can be done, it may be a good way to work the DHT11/DHT22

edit:
1. that chart is wrong as the master (mcu) pulling down the bus at end of the start signal. it should be pulled down by the slave (the device).
that is actually from the datasheet, the solid black pulse at the beginning is the start signal (i.e. sent by MCU).
Then the brown signals are the data signals from DHT11/DHT22, so indeed, the DHT11/DHT22 is controlling the line after the initial start signal pulse from the mcu.

i'm taking a look at uart signals, comparing
Image
vs
Image

the tricky part it seemed would be to deal with the start and stop bits in the frame as the DHT11/DHT22 signals won't fit within an '8 bit' frame.
I'm not sure if in addition, there may be 'gaps' between the serial frames. And that the DHT11/DHT22 signal would likely stretch beyond a single '8 bit' serial frame.
xlwww
Posts: 14
Joined: Tue Dec 27, 2022 9:19 am

Re: Question about Bluepill arduino using OnePulse mode

Post by xlwww »

This is a good idea, how should I use DMA to capture the pulse width.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Question about Bluepill arduino using OnePulse mode

Post by dannyf »

that is actually from the datasheet,
the datasheet is likely wrong there. Chart 3 shows that the slave pulls down the bus instead. it is likel that this is an open-drain configuration. so the high state of the start signal is simply the master let go of the bus.
i'm taking a look at uart signals
the challenging part here is that the bit0 and bit1 transmission has variabl length.

if you are happy with just measuring the length of the high signal, the simplest might be to use a pin change interrupt:

Code: Select all

pin_change isr:
  static uint32_t tick=0;  //tick of the edges
  //clear the flag
  if (pin is high) {tick = ticks();}  //this is a rising edge
  if (pin is low) {if (ticks() - tick > DHT_50us) received bit 1; else received bit 0;}
  
also, as the length of the entire pulse is also variable, you can simply measure the falling edges to determine if a bit 1 (120us) or a bit 0 (80us) has been transmitted.
xlwww
Posts: 14
Joined: Tue Dec 27, 2022 9:19 am

Re: Question about Bluepill arduino using OnePulse mode

Post by xlwww »

I think that external pin interrupts will bring more complex code logic and code layout requirements, so I think it seems more appropriate to use a timer. I want DMA to pass the captured data directly into the array, but I didn't find the relevant code, I think this will double the efficiency
ag123
Posts: 1668
Joined: Thu Dec 19, 2019 5:30 am
Answers: 25

Re: Question about Bluepill arduino using OnePulse mode

Post by ag123 »

@dannyf I think the pin change interrupt is also a good approach, e.g. using EXTI, but that i've not really studied it (ext (pin) interrupts).
it is particularly useful if exti can detect the edges rather than just level changes.

edit:
I read a little into RM0008 stm32f10x ref manual
https://www.st.com/resource/en/referenc ... ronics.pdf
EXTI is apparently edge triggered.
hence, I'd guess it would work well, e.g. along with a timer or simply using micros() or ticks() to measure the duration.

---
xlwww wrote: Mon Jan 02, 2023 3:37 pm This is a good idea, how should I use DMA to capture the pulse width.
i've been thinking about 2 approaches:
1) timer with dma

Accordingly, stm32 timers can drive/trigger dma, this would take some reading up in the ref manuals etc.
In one of the 'projects' that i did is to use timer triggering dma to read a sequence of gpios. 8 parallel bits
it is a little logic analyzer based on stm32f401cc (black pill)
viewtopic.php?t=116
https://github.com/ag88/SumpSTM32F401cc
note that this is based on libmaple ('rogers' or 'steves') core.

code that deals with the timer and dma is in the Backend.cpp class
https://github.com/ag88/SumpSTM32F401cc ... ackend.cpp

you can literally try this out if you have a stm32f401ccu (black pill) board.
e.g. https://stm32-base.org/boards/STM32F401 ... -Pill-V3.0

it is quite possible to port this to stm32f103, but that I've not studied it.

In this case, what you are doing is to read a regularly timed set of data into a buffer, and you need to process this buffer to decode that into the DHT11/DHT22 data outputs.

2) spi

This is actually the same as the timer with dma approach, just that you are using SPI to read the data instead.
hence, what you are doing here is to read regularly timed inputs into a buffer, and you need to decode that into the DHT11/DHT22 data outputs.

using SPI is possibly 'simplier', but I think similarly, the Arduino SPI api is very much 'blocking' i.e. reads 1 byte or block of data at a time.
e.g. using the int8_t in =SPI.transfer(int8_t out); call.

I think it is quite possible to use SPI with interrupts and/or even dma, but that it would likely need a custom implementation instead of just using the arduino spi api.

This 'hardware driven' approach, has the benefit that it is somwhat 'async', dma reads the data and puts it in a buffer, and you can process it later.
But that it means allocating memory for the (dma) buffer, this is quite costly on microcontrollers which has low sram.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: Question about Bluepill arduino using OnePulse mode

Post by dannyf »

if you want to use spi (or usart), it is effectively sampling a (dht) bit -> aka you are sampling the waveform at the spi's bps rate. you can achieve that by sampling the gpio, with or without dma. parsing the data will require knowing the sampling rate and the sampling rate will need to be consistant.

its advantage is simplicity, and its disadvantage is ram usage.
Post Reply

Return to “General discussion”