This Ultrasonic distance parking assistant was an upgrade from a previous design I had. It still uses the HC-SR04 ultrasonic sensor. But I wanted one were the I wrote every part of the code, including bit banging the SPI communication to the LED Matrix (MAX7219). Actually I had to because the built in SPI tied up a pin I needed elsewhere. This actually worked out quit well, but the measurement on the echo was difficult. I know there are better ways using timer counter.. But I ended up using just the timer with my own counter, And it was accurate and works. I also had to write my own custom font for the display because I wanted 2 digit display squeezed in on one 8x8 led display. If you want the Arduino version just google it, its easy.. This one is not. But works great and all the mistakes are mine. 



Breadboard Proto type hooked up to 4 channel scope.  Also tested using USART hooked up to FTDI (just TX and ground) to read the timer counter and conversion from counts to CM to inches.

Below is the Schematics. For the Caps I used 100nf and 10uf (about standard), Its a very simple circuit.

Here is the code I came up with for this. Please go easy on me.. I built it from scratch. I originally used the built in SPI, but found that interfered with  a pin to much (dedicated MISO). So just bit banged the SPI with works great. The measuring the pulse was the real difficult part. I know there is a built in counter. BUT I'm still learning the new 1, 0, and 2 series of the ATtiny microcontrollers. It is in my with list to use counter/timer. For now I use the timer interrupt and my own counter, which has measured spot on with a yard stick.

I do not have an Arduino port of this, I compiled this with Microchip Studio. If you want Arduino version just google its a simple set of code which I had on my first version with a 328p and OLED display. But the OLED started to fade and was difficult for my old eyes to see. This new version, almost a blind person could see. Its Brightness is only set to 5 out of 1-15 and that is very bright..

/* * ATtiny402-Ultrasonic-with-MAX7219.c * * IT WORKS Great!!!  * Detects out to 60 inches then guides you in.. * * Update 6/12/2023: There is a bug, looses range and gives random results.. * Found it was the wires interfering with the sensor.. as well as out of range. * Update 6/17/2023: updated resultCount for time to sleep.. have not tested.. * Update 6/18/2023: resultCount works great, extended time to about 3-4 seconds. also * found out the glitch of random numbers when out of range or on occasion was a power drop, * added 1 1000uf cap and 1 450uf cap sovled problem. * * wish list: * each time in the timer, then I can have a more accurate pulse count. * also want to change my custom counter to using TCB0.CNT with pin change interrupt. * * Created: 6/9/2023 16:54:41 * Author : Sherman Stebbins */ 
#include <avr/io.h>#include <avr/interrupt.h>#define F_CPU 20000000UL //had to put this for delay.. #include <util/delay.h>#include "font.h"//Why does this affect PIN7 (echo pin).. //It should not but putting faster 0x005f was about as fast I it could go. about 200khz, if at 0x0001 was about 300khz.#define TCB_TIMEOUT_VALUE 0x0108 //0x0108 DONT screw with this!!!! But I want to..
#define TriggerPin PIN2_bm#define EchoPin PIN7_bm#define MOSI_pin PIN1_bm#define SCK_pin PIN3_bm#define CS_pin PIN6_bm#define SQ_big 37#define SQ_medBig 38#define SQ_med 39#define SQ_small 40
void TCB0_init (void);void sendSPI(uint8_t address,uint8_t data); //send address and Data to MAX7219 8x8 displayvoid initDisplay(void);void clearDisplay(void);void sendByte(uint8_t data);
volatile uint16_t timerCount = 0; //maybe change to long int..volatile uint8_t shutDown = 0;volatile uint8_t resultCount = 0;volatile uint8_t resultPrevious = 0;
//set close to 37khz ISR(TCB0_INT_vect){ TCB0.INTFLAGS = TCB_CAPT_bm; /* Clear the interrupt flag */ if(PORTA.IN & EchoPin){ timerCount++; }}
int main(void){ CCP=0XD8; CLKCTRL_MCLKCTRLB = 0b00000000; while(!(CLKCTRL.MCLKSTATUS & 0x10)){} //wait for sync CLKCTRL_OSC20MS_bm same as 0x10 PORTA.DIR = MOSI_pin|SCK_pin|CS_pin|TriggerPin; //don't remove pin0 because of built in spi (don't have built in spi any more :) PORTA.DIR &= ~(EchoPin); //set EchoPin to input PORTA.OUT = CS_pin; //put CS high initDisplay(); TCB0_init(); sei(); clearDisplay(); _delay_ms(30); for(uint8_t j=0;j<4;j++){ for (uint8_t i= 0; i<8;i++){ sendSPI(i+1,font[j][i]); } _delay_ms(300); } _delay_ms(20); clearDisplay(); _delay_ms(500); while (1) { timerCount = 0; PORTA.OUT |= TriggerPin; //trigger Sensor _delay_us(10); PORTA.OUTCLR = TriggerPin;// TCB0_init(); //timerCount = 0; // while(PORTA.IN & EchoPin){;} //try this now its changed to pin7 //this would not work..     //this was at 20ms, but data sheet recommended >60 _delay_ms(90); //wait for result from ultrasonic sensor in timer B// TCB0.CTRLA = 0; float bigNum = timerCount; //results from how long pin1 was high (echo pin) timerCount = 0; bigNum = bigNum/4; uint16_t result = bigNum * 0.393701; bigNum = 0; //result = result*0.393701; if(result < 85 && shutDown==0){     if (result < 85 && result > 65 ){         for (uint8_t i= 0; i<8;i++){         sendSPI(i+1,font[SQ_small][i]);         }     }else if(result <= 65 && result > 55){ //want to impement, but need full range.         for (uint8_t i= 0; i<8;i++){         sendSPI(i+1,font[SQ_med][i]);         }     }else if(result <= 55 && result > 45 ){         for (uint8_t i= 0; i<8;i++){         sendSPI(i+1,font[SQ_medBig][i]);         }     }else if(result <= 45 && result > 36){         for (uint8_t i= 0; i<8;i++){         sendSPI(i+1,font[SQ_big][i]);         }     }else if(result <= 36){  //just spit out result in inches now         for (uint8_t i= 0; i<8;i++){          sendSPI(i+1,font[result][i]);         }     } if(result == resultPrevious){ //time it takes to shutdown display resultCount++; if (resultCount > 35){ //25 is about 2 seconds. resultCount = 0; shutDown = 1; } } resultPrevious = result;     _delay_ms(10); }else{ for (uint8_t i= 0; i<8;i++){ sendSPI(i+1,font[41][i]); } if (result != resultPrevious){ shutDown = 0; } } result = 0; _delay_ms(20); //Data sheet recommends 10ms before next pulse. }}
void TCB0_init (void){ TCB0.CCMP = TCB_TIMEOUT_VALUE; //Load the Compare or Capture register with the timeout value TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //Enable TCB and set CLK_PER divider to 1 (No Prescaling) TCB0.CTRLB = 0;// TCB_CNTMODE_INT_gc; //Configure TCB in Periodic Timeout mode TCB0.INTCTRL = TCB_CAPT_bm; //Enable Capture or Timeout interrupt// TCB0.EVCTRL = TCB_CAPTEI_bm | TCB_EDGE_bm; //Enable Event Input and Event Edge}
void sendSPI(uint8_t address,uint8_t data){ PORTA.OUTCLR = CS_pin; //set CS low sendByte(address);  //send high bite sendByte(data);  //send low bite PORTA.OUT = CS_pin; //set CS high // _delay_us(10); //this worked as time test for removing while loop.. but not for 0xff issue}
void sendByte(uint8_t value){ uint8_t i; __asm("nop"); for (i = 0; i < 8; ++i) { PORTA.OUT &= ~(SCK_pin); //clock low // __asm("nop"); if (value & 0x80) { PORTA.OUT |= (MOSI_pin); //DIN high } else { PORTA.OUT &= ~(MOSI_pin); //DIN low } PORTA.OUT |= (SCK_pin); //clock high value = value << 1; }}
void initDisplay(void){ sendSPI(0x09,0x00); //decode mode sendSPI(0x0a,0x05); //light intensity (0-15) //5 is best for visibility in garage at a distance. sendSPI(0x0b,0x07); //Scan limit on how many rows.. sendSPI(0x0f,0x00); //disable display test  sendSPI(0x0c,0x01); //Shutdown set to run normal}
// This is clear matrix function.void clearDisplay(void){ for(uint8_t x=1;x<9;x++){ sendSPI(x,0x00); }}
// Tested Great!!!! (but caused problems when tried to add ultra sonic sensor, so I bit banged the SPI)//Send the Address and Data to MAX7219/*void sendSPI(uint8_t address,uint8_t data){ SPI0.CTRLA = SPI_MASTER_bm|SPI_ENABLE_bm; PORTA.OUTCLR = CS_pin; //Put CS/SS Low SPI0.DATA = address;  //send high bite while(!(SPI0.INTFLAGS & SPI_IF_bm)){} //wait for data SPI0.DATA = data;  //send low bite while(!(SPI0.INTFLAGS & SPI_IF_bm)){} //wait for data // _delay_us(10); //this worked as time test for removing while loop.. but not for 0xff issue PORTA.OUT = CS_pin; //Put CS/SS High SPI0.CTRLA = 0; //turn off SPI}*/

Here is the font.h file for the custom fonts. I wanted number from 0-36 and a few sizes of squares

/*** font.h* Custom fonts for MAX7219 with 8x8 LED Display* Numbers 0-36 and 4 square sizes.* To be used with Ultrasonic sensor for Garage distance parking.** Sherman Stebbins * 2023/06/09*/

const uint8_t font[42][8]={ {   0b00111100,//0 0b01000010, 0b01000010, 0b01000010, 0b01000010, 0b01000010, 0b01000010, 0b00111100}, {   0b00001000,//1 0b00011000, 0b00001000, 0b00001000, 0b00001000, 0b00001000, 0b00001000, 0b00011100}, { 0b00111000,//2 0b01000100, 0b00000100, 0b00000100, 0b00001000, 0b00010000, 0b01000000, 0b01111110}, { 0b01111100,//3 0b00000010, 0b00000010, 0b00001100, 0b00000010, 0b00000010, 0b00000100, 0b01111000}, { 0b01000010,//4 0b01000010, 0b01000010, 0b01111110, 0b00000010, 0b00000010, 0b00000010, 0b00000010}, { 0b01111110,//5 0b01000000, 0b01000000, 0b01111110, 0b00000010, 0b00000010, 0b00000010, 0b01111110}, { 0b01111110,//6 0b01000000, 0b01000000, 0b01111110, 0b01000010, 0b01000010, 0b01000010, 0b01111110}, { 0b01111110,//7 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b01000000}, { 0b00111100,//8 0b01000010, 0b01000010, 0b00111100, 0b01000010, 0b01000010, 0b01000010, 0b00111100},
{ 0b01111110,//9 0b01000010, 0b01000010, 0b01111110, 0b00000010, 0b00000010, 0b00000010, 0b00000010}, { 0b10011110,//10 0b10100001, 0b10100001, 0b10100001, 0b10100001, 0b10100001, 0b10100001, 0b10011110}, { 0b10000010,//11 0b10000010, 0b10000010, 0b10000010, 0b10000010, 0b10000010, 0b10000010, 0b10000010}, { 0b10011110,//12 0b10100001, 0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b10010000, 0b10111111}, { 0b10111110,//13 0b10000001, 0b10000001, 0b10000110, 0b10000010, 0b10000001, 0b10000001, 0b10011110}, { 0b10100001,//14 0b10100001, 0b10100001, 0b10111111, 0b10000001, 0b10000001, 0b10000001, 0b10000001}, { 0b10111111,//15 0b10100000, 0b10100000, 0b10111110, 0b10000001, 0b10000001, 0b10000001, 0b10111110}, { 0b10001110,//16 0b10010000, 0b10100000, 0b10111110, 0b10100001, 0b10100001, 0b10100001, 0b10011110}, { 0b10111111,//17 0b10000001, 0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b10010000, 0b10100000}, { 0b10011110,//18 0b10100001, 0b10100001, 0b10011110, 0b10100001, 0b10100001, 0b10100001, 0b10011110}, { 0b10011110,//19 0b10100001, 0b10100001, 0b10011110, 0b10000001, 0b10000001, 0b10000001, 0b10111110}, { 0b01000110,//20 0b10101001, 0b00101001, 0b00101001, 0b01001001, 0b10001001, 0b10001001, 0b11100110}, { 0b01000001,//21 0b10100011, 0b00100001, 0b00100001, 0b01000001, 0b10000001, 0b10000001, 0b11100011}, { 0b01000010,//22 0b10100101, 0b00100001, 0b00100001, 0b01000010, 0b10000100, 0b10000100, 0b11100111}, { 0b01000110,//23 0b10101001, 0b00100010, 0b00100100, 0b01000010, 0b10000001, 0b10001001, 0b11100110}, { 0b01001001,//24 0b10101001, 0b00101001, 0b00101001, 0b01001111, 0b10000001, 0b10000001, 0b11100001}, { 0b01001111,//25 0b10101000, 0b00101000, 0b00101000, 0b01001110, 0b10000001, 0b10000001, 0b11101110}, { 0b01000001,//26 0b10100010, 0b00100100, 0b00101000, 0b01001110, 0b10001001, 0b10001001, 0b11101110}, { 0b01001111,//27 0b10100001, 0b00100001, 0b00100010, 0b01000100, 0b10001000, 0b10001000, 0b11101000}, { 0b01000110,//28 0b10101001, 0b00101001, 0b00100110, 0b01001001, 0b10001001, 0b10001001, 0b11100110}, { 0b01000110,//29 0b10101001, 0b00101001, 0b00101001, 0b01000111, 0b10000001, 0b10000001, 0b11100001}, { 0b11100110,//30 0b00101001, 0b00101001, 0b11101001, 0b00101001, 0b00101001, 0b00101001, 0b11100110}, { 0b11100001,//31 0b00100011, 0b00100001, 0b11100001, 0b00100001, 0b00100001, 0b00100001, 0b11100001}, { 0b11100110,//32 0b00101001, 0b00100001, 0b11100010, 0b00100100, 0b00101000, 0b00101000, 0b11101111}, { 0b11101110,//33 0b00100001, 0b00100010, 0b11100100, 0b00100010, 0b00100001, 0b00100001, 0b11101110}, { 0b11101001,//34 0b00101001, 0b00101001, 0b11101111, 0b00100001, 0b00100001, 0b00100001, 0b11100001}, { 0b11101111,//35 0b00101000, 0b00101000, 0b11101111, 0b00100001, 0b00100001, 0b00100001, 0b11101110}, { 0b11100011,//36 0b00100100, 0b00101000, 0b11101000, 0b00101110, 0b00101001, 0b00101001, 0b11100110}, { 0b11111111,//full square //37 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b11111111}, { 0b00000000,// 3/4 square //38 0b01111110, 0b01000010, 0b01000010, 0b01000010, 0b01000010, 0b01111110, 0b00000000}, { 0b00000000,// 1/2 square //39 0b00000000, 0b00111100, 0b00100100, 0b00100100, 0b00111100, 0b00000000, 0b00000000}, { 0b00000000,// 1/4 square //40 0b00000000, 0b00000000, 0b00011000, 0b00011000, 0b00000000, 0b00000000, 0b00000000}, { 0b00000000,// one dot //41 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000},