Open kingodragon opened 1 year ago
Yes, BlueVGA depends on Timers to create the VGA signals correctly.
Systick ISR
runs an interrupt every 1 ms and it causes the screen to glitch.
BlueVGA uses Timer1 for the scanline with an interrupt at 31.46875 kHz and Timer4 to count 525 scanlines, which in turn makes 1 frame that takes 16.6666 ms (1/60 second). One scanline takes 31.77756 microseconds.
Therefore, we may think in this way:
You may want to expose scanLineCounter
used in sendScanLine(void)
, in the same way frameNumber
works.
This way, 18ms will be about 566 scan lines, in time measuring...
The necessary changes to the Source Code would be:
Turn scanLineCounter
a volatile external variable, like frameNumber
.
Remove Line 183 from https://github.com/RoCorbera/BlueVGA/blob/Graph-Pixels/src/bluevgadriver.c#L177
Add Line 59 like this volatile uint32_t scanLineCounter = 0;
right after Line 58
https://github.com/RoCorbera/BlueVGA/blob/Graph-Pixels/src/bluevgadriver.c#L58
Remove Line https://github.com/RoCorbera/BlueVGA/blob/Graph-Pixels/src/bluevgadriver.c#L194 to keep it counting.
Everything must be done in sync, in order that the first thing your software does is to set the line low and start counting the number of scanlines.
For that, you may just send the DHT-11 Start signal right after a vga.waitVSync(1)
.
This is important because the sketch never runs when the VGA driver is drawing the screen, therefore, the process of scanline counting can't just end at any time. The sketch runs in the VGA Vertical blanking interval
pinMode(DHT11, OUTPUT);
vga.waitVSync(1); // sync the start of the pulse with the VGA VBLANK beginging.
//scanLineCunter would be exposed as described above.
// The sketch needs to declare: extern volatile uint32_t scanLineCounter;
uint32_t scanLine_18ms = scanLineCounter + 566; // 566 x 31.77756 us = 18ms
digitalWrite(DHT11, LOW);
while (scanLineCounter < scanLine_18ms); // wait 18ms
// this may take a couple microseconds, therefore, it may need to reduce 566 to 560 instead...
// better check all timing with some logic data analyser
digitalWrtie(DHT11, HIGH);
// now start TIMER2 capture, etc.
Response();
// at the end of the DHT11 response, disable TIMER2 to stop the screen glitch.
A second problem would be the function Response()
that uses Timer2 ISR...
It also depends on interrupts and it will also glitch the screen.
But it would be done very fast and for a short time.
Maybe not a big problem for your application.
I have changed the code to add getScanLineNumber()
.
Using the source code from github main branch, it would be like that:
pinMode(DHT11, OUTPUT);
vga.waitVSync(1); // sync the start of the pulse with the VGA VBLANK beginging.
//scanLineCunter would be exposed as described above.
// The sketch needs to declare: extern volatile uint32_t scanLineCounter;
uint32_t scanLine_18ms = vga.getScanLineNumber() + 566; // 566 x 31.77756 us = 18ms
digitalWrite(DHT11, LOW);
while (vga.getScanLineNumber() < scanLine_18ms); // wait 18ms
// this may take a couple microseconds, therefore, it may need to reduce 566 to 560 instead...
// better check all timing with some logic data analyser
digitalWrtie(DHT11, HIGH);
// now start TIMER2 capture, etc.
Response();
// at the end of the DHT11 response, disable TIMER2 to stop the screen glitch.
You may get the changes by cloning master branch. downloading the zip file or by pulling from it, using git pull
The Zip File with the master branch latest code can be obtained from https://github.com/RoCorbera/BlueVGA/archive/refs/heads/master.zip
@kingodragon - Actually it may not work... VBLANK is just 35 scanlines per frame. Therefore waiting for 566 scanlines means 525 + 41 scanlines, which will make the end of the 18ms to out of the 2nd VBLANK interval...
The MCU also runs just a few cycles of the sketch per scanline in the HBLANK remaining time.
Maybe if you use GPIO->ODR
instead of digitalWrite()
to change the state of pin DHT11
, it may need just a few cycles and work....
As you can see, BlueVGA uses 90% of the CPU just to drive the VGA signals... there is very few CPU for the other needs. Another alternative would be try to generate the 18ms pulse also using TIM2 instead of doing it using software bitbang.
I hope you figure out a way to make it work. Please post here your final solution for other users.
I have changed the code to add
getScanLineNumber()
. Using the source code from github main branch, it would be like that:pinMode(DHT11, OUTPUT); vga.waitVSync(1); // sync the start of the pulse with the VGA VBLANK beginging. //scanLineCunter would be exposed as described above. // The sketch needs to declare: extern volatile uint32_t scanLineCounter; uint32_t scanLine_18ms = vga.getScanLineNumber() + 566; // 566 x 31.77756 us = 18ms digitalWrite(DHT11, LOW); while (vga.getScanLineNumber() < scanLine_18ms); // wait 18ms // this may take a couple microseconds, therefore, it may need to reduce 566 to 560 instead... // better check all timing with some logic data analyser digitalWrtie(DHT11, HIGH); // now start TIMER2 capture, etc. Response(); // at the end of the DHT11 response, disable TIMER2 to stop the screen glitch.
You may get the changes by cloning master branch. downloading the zip file or by pulling from it, using
git pull
The Zip File with the master branch latest code can be obtained from https://github.com/RoCorbera/BlueVGA/archive/refs/heads/master.zip
Thank you for your patient answer, but I don't know if it is my illusion, it seems to be similar to the tearing caused by using delay(18). Also I added a SSD1306 using hardware I2C1 using U8G2 library. Although they all work fine, but bring a huge moment of tearing, I can only use vga.clearScreen();
to make it black for a moment to increase the degree of smoothness, and if I don't use systick_enable();
Neither will work and I'm depressed.
Ok thanks, I'll try it later
I tried to put the Timer2 channel1 in OnePulse mode first, and then turn it into capture mode at the end of the pulse, but I searched all afternoon and couldn't find a working code. I don't know how to configure those complicated registers. I don't know why the following code doesn't work.
void Request() /* Microcontroller send request */
{
pinMode(DHT11, PWM);
// digitalWrite(DHT11, LOW);
// delay(18); /* wait for 18ms */
uint16_t pulseDelay = 20000;
uint16_t pulseWidth = 19000;
Timer2.pause(); // stop the timers before configuring them
timer_oc_set_mode(TIMER2, 1, TIMER_OC_MODE_PWM_1, TIMER_OC_PE);
Timer2.setPrescaleFactor(72); // 1 microsecond resolution
Timer2.setOverflow(pulseWidth + pulseDelay-1);
Timer2.setCompare(TIMER_CH1, pulseDelay);
// counter setup in one pulse mode, as slave triggered by External input for Timer 2
TIMER2_BASE->CR1 = ( TIMER_CR1_OPM ); // one pulse mode
TIMER2_BASE->SMCR = ( TIMER_SMCR_TS_ETRF | TIMER_SMCR_SMS_TRIGGER );
TIMER2_BASE->CCER = TIMER_CCER_CC1E ; // enable channels 1
Timer2.attachInterrupt(TIMER_CH1,handler_channel_1);
Timer2.refresh(); // start timer 2
Timer2.resume(); // let timer 2 run
// digitalWrite(DHT11,HIGH); /* set to high pin */
}
void handler_channel_1(void)
{
// turn the timer into capture mode
}
I think that it is very hard to have VGA and anything else that needs specific delay()
or Hardware Peripherals runing in the same STM32F103 that will not cause screen tearing.
As I said, 90% of the CPU time is dedicated to generate VGA signals and it depends on the Interrupts, like sendScanLine()
working at the exact timing. Otherwise the screen will not be crystal clear.
Given that, the only way to make peripherals (I2C, SPI, PWM, Pulses, UART, etc) to work would be by using pure Hardware and DMA with no Software ISR. The sketch would just check the register status and then process the read data, or fill up SRAM for a DMA to send it to the peripheral.
In this case, no Arduino Library would work as desired and a whole new set of tailor made HAL libraries would be needed.
Other than that, there may be a few possible ways to go:
1) Create a sort of a synchronous protocol based on SPI to comunicate the STM32F103 VGA device with another STM32 or any other MCU that would actually run all the peripherals and just report back to the STM32 VGA MCU. This would be a dual MCU solution, like a dual Core with a communications channel between them.
2) Use a monocromatic VGA aproach like those sending SPI pixels in 1bpp. Since this is a more hardware based aproach, it would be better suited for using it at the same time with other Arduino Libraries. It would have a minimal screen tearing, but it will be just one color VGA. Check this https://www.artekit.eu/vga-output-using-a-36-pin-stm32/
3) Move to another MCU such as ESP32, which is dual core and it has a way better bus and set of peripherals that can run in a multimatrix bus. ESP32 has an Arduino VGA Library at https://github.com/bitluni/ESP32Lib
I think that it is very hard to have VGA and anything else that needs specific
delay()
or Hardware Peripherals runing in the same STM32F103 that will not cause screen tearing.As I said, 90% of the CPU time is dedicated to generate VGA signals and it depends on the Interrupts, like
sendScanLine()
working at the exact timing. Otherwise the screen will not be crystal clear.Given that, the only way to make peripherals (I2C, SPI, PWM, Pulses, UART, etc) to work would be by using pure Hardware and DMA with no Software ISR. The sketch would just check the register status and then process the read data, or fill up SRAM for a DMA to send it to the peripheral.
In this case, no Arduino Library would work as desired and a whole new set of tailor made HAL libraries would be needed.
Other than that, there may be a few possible ways to go:
- Create a sort of a synchronous protocol based on SPI to comunicate the STM32F103 VGA device with another STM32 or any other MCU that would actually run all the peripherals and just report back to the STM32 VGA MCU. This would be a dual MCU solution, like a dual Core with a communications channel between them.
- Use a monocromatic VGA aproach like those sending SPI pixels in 1bpp. Since this is a more hardware based aproach, it would be better suited for using it at the same time with other Arduino Libraries. It would have a minimal screen tearing, but it will be just one color VGA. Check this https://www.artekit.eu/vga-output-using-a-36-pin-stm32/
- Move to another MCU such as ESP32, which is dual core and it has a way better bus and set of peripherals that can run in a multimatrix bus. ESP32 has an Arduino VGA Library at https://github.com/bitluni/ESP32Lib
Yes, ESP32 has excellent performance and can better complete this project. However, I'm a student and I'm finishing my STM32 course design, and the course is mandated to be done only with STM32 MCU. So I need to connect as many devices as possible to get a higher course score, which is difficult. I went through the code of my DHT11 part yesterday and I think there is no logical problem, but it just doesn't work. I added diagrams in the code comments, I can see from reading your code that your understanding of the MCU is very deep. So can you please help me to check it? Thank you very much.
void Request() /* Microcontroller send request */
{
uint16_t PulseLOW = 20000; // should > 18ms
uint16_t PulseHIGH = 18000; // not important
pinMode(DHT11, PWM);
Timer2.pause(); // stop the timers before configuring them
timer_oc_set_mode(TIMER2, 1, TIMER_OC_MODE_PWM_2, TIMER_OC_PE);//set PWM Mode2,and enable
Timer2.setPrescaleFactor(72); // 1 microsecond resolution
Timer2.setOverflow(PulseLOW + PulseHIGH-1);
Timer2.setCompare(TIMER_CH1, PulseLOW);
// Triggered after PulseLOW
Timer2.attachInterrupt(TIMER_CH1,Response);
// counter setup in one pulse mode
TIMER2_BASE->CR1 = ( TIMER_CR1_OPM ); // one pulse mode
// as slave triggered by External input for Timer 2,
//I don't quite understand this sentence and commented it
//TIMER2_BASE->SMCR = ( TIMER_SMCR_TS_ETRF | TIMER_SMCR_SMS_TRIGGER );
TIMER2_BASE->CCER = TIMER_CCER_CC1E ; // enable channels 1
Timer2.refresh(); // start timer 2
Timer2.resume(); // let timer 2 run
/*
Using PWM Mode 2,OnePulse Mode but not work
_______20MS__________-------------------18MS-------------------___________
^ ^ ^
|<----PulseLOW------>|<-- PulseHIGH, but don't need attention->|
^
|
Compare1Interrupt
execution Response(),
Change the mode of the Timer2
*/
}
void Response()
{
Timer2.detachInterrupt(TIMER_CH1);
pinMode(DHT11, INPUT_PULLUP);
Timer2.attachInterrupt(TIMER_CH1,handler_channel_1);
//Timer2.attachCompare1Interrupt(handler_channel_1);
TIMER2_BASE->CR1 = TIMER_CR1_CEN;
TIMER2_BASE->CR2 = 0;
TIMER2_BASE->SMCR = 0;
TIMER2_BASE->DIER = TIMER_DIER_CC1IE;
TIMER2_BASE->EGR = 0;
TIMER2_BASE->CCMR1 = TIMER_CCMR1_CC1S_INPUT_TI1;
TIMER2_BASE->CCMR2 = 0;
TIMER2_BASE->CCER = TIMER_CCER_CC1E;
TIMER2_BASE->PSC = 71;
TIMER2_BASE->ARR = 0xFFFF;
TIMER2_BASE->DCR = 0;
}
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.
a[index1++]=channel_1; //Record the duration of the data returned by DHT11 to judge logic 0 and 1, and save it in an array
}
}
I had to read the Reference Manual and do a lot of experimentation in order to make the Timer1 and Timer4 work as desired. That was almost 2 years ago and I don't work with STM32 any more.
I can tell that the "recipe" for making One Time Pulse to work is in the page 328 and 329 of the manual
I had to read the Reference Manual and do a lot of experimentation in order to make the Timer1 and Timer4 work as desired. That was almost 2 years ago and I don't work with STM32 any more.
I can tell that the "recipe" for making One Time Pulse to work is in the page 328 and 329 of the manual
Thank you, I found the reason, when using DHT11, the pin should be pulled high firstpinMode(DHT11, OUTPUT); digitalWrite(DHT11,HIGH);
, and then pulled low for 18MS, it is very important! But I delayed with timer and disabled IIC SSD1306, VGA still tearing. I guess the capture mode interrupt of timer2 affects the progress of VGA. The following is the code that can be run,most of them use Timer2 to drive.
void loop()
{
Request();
vga.clearScreen(); //swipe the screen to avoid tearing caused by interruption
vga.waitVSync(3); // Wait for 3*16MS to receive data
/*
Process the received data, then write to VGA
*/
}
void Request() /* Microcontroller send request */
{
/*Very important!!!!!!!!!!!*/
pinMode(DHT11, OUTPUT);
digitalWrite(DHT11,HIGH);
/*Very important!!!!!!!!!!!*/
uint16_t PulseLOW = 18000; // should > 18ms
uint16_t PulseHIGH = 30; // not important
pinMode(DHT11, PWM);
Timer2.pause(); // stop the timers before configuring them
timer_oc_set_mode(TIMER2, 1, TIMER_OC_MODE_PWM_2, TIMER_OC_PE);//set PWM Mode2,and enable
Timer2.setPrescaleFactor(72); // 1 microsecond resolution
Timer2.setOverflow(PulseLOW + PulseHIGH-1);
Timer2.setCompare(TIMER_CH1, PulseLOW);
// Triggered after PulseLOW
Timer2.attachInterrupt(TIMER_CH1,Response);
// counter setup in one pulse mode
TIMER2_BASE->CR1 = ( TIMER_CR1_OPM ); // one pulse mode
TIMER2_BASE->CCER = TIMER_CCER_CC1E ; // enable channels 1
Timer2.refresh(); // start timer 2
Timer2.resume(); // let timer 2 run
/*
Using PWM Mode 2
_______20MS__________-------------------3MS--------------------___________
^ ^ ^
|<----PulseLOW------>|<-- PulseHIGH, but don't need attention->|
^
|
Compare1Interrupt
execution Response(),
Change the mode of the Timer2
*/
}
void Response()
{
Timer2.detachInterrupt(TIMER_CH1);
pinMode(DHT11, INPUT_PULLUP);
Timer2.attachInterrupt(TIMER_CH1,handler_channel_1);
TIMER2_BASE->CCR1=0;
TIMER2_BASE->CR1 = TIMER_CR1_CEN;
TIMER2_BASE->CR2 = 0;
TIMER2_BASE->SMCR = 0;
TIMER2_BASE->DIER = TIMER_DIER_CC1IE;
TIMER2_BASE->EGR = 0;
TIMER2_BASE->CCMR1 = TIMER_CCMR1_CC1S_INPUT_TI1;
TIMER2_BASE->CCMR2 = 0;
TIMER2_BASE->CCER = TIMER_CCER_CC1E;
TIMER2_BASE->PSC = 71;
TIMER2_BASE->ARR = 0xFFFF;
TIMER2_BASE->DCR = 0;
}
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;
}
}
My goodness, what the hell is going on here? ? ? ? Because of debugging, I added a completely unrelated variable, and the code can run completely normally, including DHT11, VGA, everything is as I expected. But when I want to delete these extraneous variables it doesn't work! ! ! ! I think it must be a matter of metaphysics, and I want to know what's going on? It seems unbelievable, but it happens, I kid you not.
This works perfectly fine!
int32_t channel_1_start, channel_1_stop, channel_1,a=0,b=0,c=0;
//This will also work
int32_t channel_1_start, channel_1_stop, channel_1;
int32_t a=0,b=0,c=0;
//This will still work as well
int32_t channel_1_start, channel_1_stop, channel_1;
int32_t d=0,e=0,f=0;
void loop(){
};
//*********Even put it here,it still work as well
int32_t d=0,e=0,f=0;
void Request(){
};
void Response(){
};
void handler_channel_1(){
};
But the following code can't drive DHT11 unexpectedly. a, b, c are completely useless variables, I even renamed them to d, e, f and it worked. But I can't delete them because DHT11 will not work properly without them.
//This will not work
int32_t channel_1_start, channel_1_stop, channel_1; //delete abc variable
//This will still not
int32_t a=0,b=0,c=0;//delete channel variable
//It seems that it will only work if the number of variables is greater than a value
void loop(){
};
void Request(){
};
void Response(){
};
void handler_channel_1(){
};
It seems that it will only work if the number of variables is greater than a value.I can only increase the variable but not decrease it, I try to increase the delay everywhere but it doesn't work, could it be caused by memory.It's like it's kidding me, I can't believe it! Have you ever encountered this problem? Sincerely ask for your advice.
Using the logic analyzer I bought yesterday, I found that it is possible to do PWM all the time to activate the DHT11 without deleting the variable. But once those "useless" variables are deleted, if DHT11 does not respond, it can still generate PWM to generate a low level of 18MS, but once DHT11 responds once, it will never generate PWM again Come to drive DHT11. This is a metaphysical question, very bad.
//*****Even put it here,it still work as well int32_t d=0,e=0,f=0;
Cortex M3 has some issues with memory fetching time and byte alligment. This issue you are facing is related to the addressing alligment of the variables.
Some times, in order to reduce the number of cycles for memory fetching and other operations, it is necessary to allign the variables to 32 bits addressing.
Because this VGA driver is very tie in timing, every cycle counts... Memory allignment reduces the number of cycles used to fetch flash and sram, helping the driver to work better.
Making VGA work correctly in Cortex M3 is not simple.
I fixed this, because sprintf
caused the overflow,but the VGA still tears. The following example can run DHT11 only through Timer2, but still seems to need systick_enable();
uint8_t DHT11=PA0;
uint8_t I_RH,D_RH,I_Temp,D_Temp,CheckSum;
uint8_t DHT11_data[50]={0},index1=0;
char temp[30]="",hum[30]="";
void setup() {
// systick_disable();
Serial2.begin(115200);
Serial2.println(F("DHTxx test!"));
pinMode(PB11, OUTPUT);
print_reg();
}
void loop() {
// put your main code here, to run repeatedly:
Timer2.detachInterrupt(TIMER_CH1);
Request() ; /* send start pulse */
delay(200);
if(index1==41&&((I_RH + D_RH + I_Temp + D_Temp) == CheckSum))
{
Receive_data();
sprintf(temp, "%s%d%c%d %s" , " ",I_Temp,'.' ,D_Temp,"C");
sprintf(hum, "%s%d%c%d %c " , " ",I_RH,'.',D_RH,'%');
Serial2.println(temp);
Serial2.println(hum);
Serial2.println("OK");
}
else
{
Serial2.println("error");
}
for(int i=0;i<50;i++)
DHT11_data[i]=0;
delay(2000);
index1=0;
}
void Request() /* Microcontroller send request */
{
pinMode(DHT11, OUTPUT);
// print_reg();
digitalWrite(DHT11,LOW);
uint16_t PulseLOW = 25000; // should > 18ms
uint16_t PulseHIGH = 30; // not important
pinMode(DHT11, PWM);
Timer2.pause(); // stop the timers before configuring them
timer_oc_set_mode(TIMER2, 1, TIMER_OC_MODE_PWM_2, TIMER_OC_PE);//set PWM Mode2,and enable
Timer2.setPrescaleFactor(72); // 1 microsecond resolution
Timer2.setOverflow(PulseLOW + PulseHIGH-1);
Timer2.setCompare(TIMER_CH1, PulseLOW);
// Triggered after PulseLOW
Timer2.attachInterrupt(TIMER_CH1,Response);
// counter setup in one pulse mode
//TIMER2_BASE->CR1 = TIMER_CR1_CEN;
TIMER2_BASE->CR1 = ( TIMER_CR1_OPM ); // one pulse mode
TIMER2_BASE->CCER = TIMER_CCER_CC1E ; // enable channels 1
Timer2.refresh(); // start timer 2
Timer2.resume(); // let timer 2 run
/*
Using PWM Mode 2
_______20MS__________-------------------18MS-------------------___________
^ ^ ^
|<----PulseLOW------>|<-- PulseHIGH, but don't need attention->|
^
|
Compare1Interrupt
execution Response(),
Change the mode of the Timer2
*/
}
void Response()
{
pinMode(DHT11, INPUT_PULLUP);
Timer2.pause();
Timer2.attachInterrupt(TIMER_CH1,handler_channel_1);
TIMER2_BASE->CCR1=0;
TIMER2_BASE->CR1 = TIMER_CR1_CEN;
TIMER2_BASE->CR2 = 0;
TIMER2_BASE->SMCR = 0;
TIMER2_BASE->DIER = TIMER_DIER_CC1IE ;
TIMER2_BASE->EGR = 0;
TIMER2_BASE->CCMR1 = 0b100000001; //Register is set like this due to a bug in the define table (30-09-2017)
TIMER2_BASE->CCMR2 = 0;
TIMER2_BASE->CCER = TIMER_CCER_CC1E ;
TIMER2_BASE->PSC = 71;
TIMER2_BASE->ARR = 0xFFFF;
}
void print_reg()
{
Serial2.println("TIMER2_BASE->CR1 ="+ (String)TIMER2_BASE->CR1+';');
Serial2.println("TIMER2_BASE->CR2 =" +(String)TIMER2_BASE->CR2+';');
Serial2.println("TIMER2_BASE->SMCR ="+ (String)TIMER2_BASE->SMCR+';');
Serial2.println("TIMER2_BASE->DIER ="+ (String)TIMER2_BASE->DIER+';') ;
Serial2.println("TIMER2_BASE->EGR = "+(String)TIMER2_BASE->EGR+';');
Serial2.println("TIMER2_BASE->CCMR1 = "+(String)TIMER2_BASE->CCMR1+';');
Serial2.println("TIMER2_BASE->CCMR2 = "+(String)TIMER2_BASE->CCMR2+';');
Serial2.println("TIMER2_BASE->CCER = "+(String)TIMER2_BASE->CCER+';') ;
Serial2.println("TIMER2_BASE->PSC = "+(String)TIMER2_BASE->PSC+';');
Serial2.println("TIMER2_BASE->ARR = "+(String)TIMER2_BASE->ARR+';');
Serial2.println("TIMER2_BASE->DCR = "+(String)TIMER2_BASE->DCR+';');
Serial2.println();
}
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);
}
}
void Receive_data() /* Receive data */
{
int q=0;
I_RH=0;
D_RH=0;
I_Temp=0;
D_Temp=0;
CheckSum=0;
for (q=1; q<9; q++)
{
if(DHT11_data[q]>30) /* If high pulse is greater than 30ms */
I_RH = (I_RH<<1)|(0x01);/* Then its logic HIGH */
else /* otherwise its logic LOW */
I_RH = (I_RH<<1);
}
for (q=9; q<17; q++)
{
if(DHT11_data[q]>30) /* If high pulse is greater than 30ms */
D_RH = (D_RH<<1)|(0x01);/* Then its logic HIGH */
else /* otherwise its logic LOW */
D_RH = (D_RH<<1);
}
for (q=17; q<25; q++)
{
if(DHT11_data[q]>30) /* If high pulse is greater than 30ms */
I_Temp = (I_Temp<<1)|(0x01);/* Then its logic HIGH */
else /* otherwise its logic LOW */
I_Temp = (I_Temp<<1);
}
for (q=25; q<33; q++)
{
if(DHT11_data[q]>30) /* If high pulse is greater than 30ms */
D_Temp = (D_Temp<<1)|(0x01);/* Then its logic HIGH */
else /* otherwise its logic LOW */
D_Temp = (D_Temp<<1);
}
for (q=33; q<41; q++)
{
if(DHT11_data[q]>30) /* If high pulse is greater than 30ms */
CheckSum = (CheckSum<<1)|(0x01);/* Then its logic HIGH */
else /* otherwise its logic LOW */
CheckSum = (CheckSum<<1);
}
}
The execution of void handler_channel_1(void)
will make the screen tear.
This ISR interferes with the VGA Timer ISR.
Anyway, it has to be done this way, otherwise it would be impossible to read the DHT11 pulses.
Why is it necessary to enable systick
? It seems possible to replace all the delay()
with some equivalent vga.waitVSync()
.
Serial2.print()
may also create some noise in the screen, but I'm not sure. UART ISR may be used by Arduino Serial
and this would also make the screen tear.
Hello, I am trying to use DHT11 to match your cool library to complete the temperature display. I found that the official DHT11 library cannot be used. I try to use the capture mode of the hardware timer to complete the driver. It works successfully, but it needs to pull it down for 18ms in the initial stage, I can't use
vga.waitVSync(2)
to complete the delay, I can only modify the source file to unlock the systick and usedelay(18)
to complete the pull down, But this seems to cause the screen to flicker. How should I solve this delay problem?Below is my arduino code.