Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

(Solved)Again problem with matrix, this time 8x32

Status
Not open for further replies.
does it still revert to CH clock halt IF using external battery?

another question, I tried to understand how code takes different characters, could you guys elaborate bit?
C:
clr();
strput("WOOF",0,0);  //from void strput? how?
Blit();
and other thing, in fontmap.h , how are those hex strings to be read? As there are like oxfb even in "space" o_O....
confused...
oh, and I noticed we don't use shiftout() in these! cool!
 
Last edited:
Re the DS1307 CH bit , It has to be cleared once at initialisation usually by writing h'00 to the seconds it will stay running while you have Vbat pin connected to a battery. ( your RTC library may do this ? ) . Data sheet explains. I did type the 85 may be no RTC osc.... Me done..
 
Hmm, I haven't researched much what actually ds1307 library does, thanks for bringing this stuff up!
 
space is the only one that should be 0,0,0,0,0,0,0,0
goto windows calculator, programmer mode, click dec button(to go to decimal base 10 mode), key in 85, underneath that you will see the binary value, see how it compares to what we put in to the back buffer:
backbuffer[0] = second; // which is those corner columns displaying seconds, hr, minute, values in a un-decoded binary format of 0-255 (the size of a byte)

that part was done to verify that charput was working ok



void pixel(signed char x,signed char y,int cond)
void strput(const char* ch, signed char x,signed char y)
void charput(unsigned char ch, signed char x,signed char y)


void at the beginning means they dont return any values, it is nice when we name our variables in "english", in this case we named x and y the position we want it to show on the screen but note the spot where we pass in the value is different so we must reflect that when making the call: pixel(3,2,1); this will put condition 1 in to the buffer at points x=3 and y=2;
also note your lcd starts at 0,0.
strput and charput are similar where Xput(value, x , y ) so again x and y are the coordinates for the startposition of what we are writing.

the main difference in strput and charput is the first value we send in:
charput(2,1,1) at position x=1 and y=1, it will call the fontmap at FONTposition 2, actually when we pass in Fpos=2 it uses this equation to find the spot in the map: disp = font[x1+(ch * 8)]; so ch is the Fpos we passed in, we multiply by 8 cause each fontChar is 8 bits long, so 2 x 8 = 16, which is where the font for number 2 starts , and then x1 loop is used to go through each of the 8 columns,
using charput(50,1,1) will call the map at position 50 which is the ascii value for keyboard number 2, and will display "2" not 50

with strput("2",1,1) again pos is x=1,y=1, but now we are passing in ascii values (google ascii table) the difference is that now it is not calling Fontmap at position 2, it is calling at position 50 or in hex 0x32, since this is the ascii value of the keyboard number 2. which is why i have the font of the number 2 at both position 2 and 50,
also using strput you can add more than 1 character so you can do this: strput("Hi2u",1,1); this makes using strings easier for when you want to send words and messages, charput is better to send values but can only be numbers from 0-9,
AGAIN!: using charput(50,1,1) will call the map at position 50 which is the ascii value for keyboard number 2, and will display "2" not 50

which is why i need to break down my seconds value to two instructions: charput((second/10),7,0);charput((second%10),15,0) , one is displaying the tens column, and one the ones column...

in summary the main difference is:
seconds = 7;
charput (seconds,0,0); // displays the "Fontmap position" from the seconds variable, ie fontmap pos 7 which is image for number 7
strput ("seconds",0,0);// "seconds" is a string cause of the quotations , not the name of a variable, so it will display the word "seconds" and thats it!

also i will repost the latest font map i did in case you are using old one
 

Attachments

  • FontMap.h
    8.5 KB · Views: 302
shiftout was replaced mostly with setpos & setdata, and handled with interrupt_handler, shiftout could have been used, but again , thats my substitute for not knowing the library well enough.. I just use my own "bit banging"
 
I now realized that I read fontmap entirely wrong, thanks for educating!
 
But, what point is with those hex values like here after letter?
C:
0x00, 0x00, 0xFF, 0x11, 0x11, 0x11, 0x01, 0x00, // 70  F   //this is letter "F" yes
0x00, 0x81, 0x21, 0xC1, 0xFF, 0x01, 0x01, 0x00, // 71   
0x00, 0x80, 0xFF, 0x18, 0x08, 0x08, 0xFF, 0x00, // 72   
0x00, 0x80, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x00, // 73   
0x00, 0x18, 0x28, 0x48, 0xfe, 0x08, 0x00, 0x00, // 74    //but what is these between?
0x00, 0xe4, 0xa2, 0xa2, 0xa2, 0x9c, 0x00, 0x00, // 75   
0x00, 0x3c, 0x52, 0x92, 0x92, 0x0c, 0x00, 0x00, // 76   
0x00, 0x80, 0x8e, 0x90, 0xa0, 0xc0, 0x00, 0x00, // 77   
0x00, 0x6c, 0x92, 0x92, 0x92, 0x6c, 0x00, 0x00, // 78   
0x00, 0x00, 0x7E, 0x81, 0x81, 0x81, 0x7E, 0x00, // 79  O //and this is "O" letter
 
I mean why array can't be like in this fashion:
C:
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //A
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //B
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //C
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //.....
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //"end"
or did iIforgot something relevant?
see file I attached, from Ian's help earlier. (tutorials)
 

Attachments

  • fnt.c
    10.1 KB · Views: 322
Last edited:
Hehe, nvm, I found out by experiencing that those stuff in between are actually letters, only need bit rewrite
and about that earlier question, you answered it earlier :)
Here's rewritten fontmap.h (capital letters correctly now, before they were bit mess but we didn't even touched them yet)
 

Attachments

  • FontMap.h.ino
    6.8 KB · Views: 319
Last edited:
ya, the ones between is the space for letters, which havnt been done yet, you can write values in binary or hex, 0b00000001 or 0x01, again feel free to change how you want
yup ascii relevant! Ians code is too, but he doesnt use the lower end of the array , he uses this line : ch -= 0x20; // characters starts a 0 not 0x20 ..... hex20 is dec32 so when he looks up font(0) he actually looks up font 32... which frees up some of that memory
we could use Ians completed map as well, if we changed how we call it with this line: disp = font[x1+(ch * 8)]; ......to this: disp = font[x1+((ch+0x20) * 8)]; ........ but we would need a code loop to rotate the font, his is upside down, ours is sideways, and may need some adjustment to retain the use of charput,
....took me a bit to understand font maps too
 
forgot the globals there..
Code:
  #include <Wire.h>
  #include "RTClib.h"
  RTC_DS1307 RTC;

int dataPin = 2;  //IC 14  //Define which pins will be used for the Shift Register control
int latchPin = 3;  //IC 12
int clockPin = 4;  //IC 11



unsigned char displayPointer=0; // for interrupt use...
unsigned char buffer1[32]; // buffer for screen
unsigned char backbuffer[32]; // Spare screen for drawing on
unsigned char power[8]={128,64,32,16,8,4,2,1};

void setcolumn(unsigned char col){
signed char pos;
for (pos = 32;pos>-1;pos--){
if (col == pos){digitalWrite(dataPin ,HIGH);}else {digitalWrite(dataPin ,LOW);} // PIN1 DATA pin
digitalWrite(clockPin ,HIGH);digitalWrite(clockPin ,LOW);
}}

void setdata(unsigned char dat){
unsigned char pos;
for (pos = 0;pos<8;pos++){
if (dat & 128){dat-=128;digitalWrite(dataPin ,HIGH);}else { digitalWrite(dataPin ,LOW);} // PIN1 DATA pin
dat = dat * 2;
digitalWrite(clockPin ,HIGH);digitalWrite(clockPin ,LOW);
}}


void Blit() //transfers data between display buffer to screen buffer
{
int addr=0;
//noInterrupts(); // disable all interrupts during setup
for(addr=0;addr < 32;addr ++)
{
buffer1[addr] = backbuffer[addr]; // put all data from display buffer
} // to screen buffer
//interrupts(); // enable all interrupts
}

void clr() //clear
{
int addr;
for(addr=0;addr<32;addr++) // Empty display buffer
backbuffer[addr]= 0;
}

void setup()
{
  Wire.begin();
  RTC.begin();
  DDRD = DDRD | B00011100;  //set pins as output
}

void loop()
{


unsigned char second = 0;
unsigned char scrollctrHR= 0;
unsigned char minute = 0;
unsigned char scrollctrMN = 0;
unsigned char hour =0;
unsigned char scrollctrSE = 0;


unsigned int cntr2 = 0;



while (1){

  DateTime now = RTC.now();

clr();

second = now.second();
minute = now.minute();
hour = now.hour();



backbuffer[0] = second;
backbuffer[1] = minute;
backbuffer[2] = hour;

backbuffer[31] = now.second();
backbuffer[30] = now.minute() ;
backbuffer[29] = now.hour();

 Blit();


 for(cntr2=0;cntr2<100 ;cntr2++){
setcolumn(displayPointer);
setdata(buffer1[displayPointer]);
digitalWrite(latchPin ,HIGH);digitalWrite(latchPin , LOW ); // STORECLOCK
if(++displayPointer==32) { displayPointer = 0; } // 32 LED row sections in total
 
delay(10);
}


}}
 
also you said it worked with code from post 253, try that again , if you still get time, means we are not loosing power/time
 
also, hopefully this one will crash:

Code:
int dataPin = 2;  //IC 14  //Define which pins will be used for the Shift Register control
int latchPin = 3;  //IC 12
int clockPin = 4;  //IC 11
//OE-GND
//MR-VCC
unsigned long previousMillis = 0;  // will store last time LED was updated
const long interval = 10;  // interval at which to blink (milliseconds)
int del = 10;
int columns = 0;
int counter = 10000;
int m = 1;
  #include <Wire.h>
  #include "RTClib.h"
  RTC_DS1307 RTC;

static uint8_t  numbers [80] =  //numbers stored here
{
  0x00, 0x7c, 0xa2, 0x92, 0x8a, 0x7c, 0x00, 0x00, // 0
  0x00, 0x42, 0xfe, 0x02, 0x00, 0x00, 0x00, 0x00, // 1
  0x00, 0x42, 0x86, 0x8a, 0x92, 0x62, 0x00, 0x00, // 2
  0x00, 0x84, 0x82, 0xa2, 0xd2, 0x8c, 0x00, 0x00, // 3
  0x00, 0x18, 0x28, 0x48, 0xfe, 0x08, 0x00, 0x00, // 4
  0x00, 0xe4, 0xa2, 0xa2, 0xa2, 0x9c, 0x00, 0x00, // 5
  0x00, 0x3c, 0x52, 0x92, 0x92, 0x0c, 0x00, 0x00, // 6
  0x00, 0x80, 0x8e, 0x90, 0xa0, 0xc0, 0x00, 0x00, // 7
  0x00, 0x6c, 0x92, 0x92, 0x92, 0x6c, 0x00, 0x00, // 8
  0x00, 0x60, 0x92, 0x92, 0x94, 0x78, 0x00, 0x00, // 9
};




void setup()
{
  Wire.begin();
  RTC.begin();
  DDRD = DDRD | B00011100;  //set pins as output
}

void loop()
{
  DateTime now = RTC.now();  
  unsigned long currentMillis = millis();  //take value from millis() each loop
  if (currentMillis - previousMillis >= interval)
  {
  previousMillis = currentMillis;
  if (counter == 0)
  {
  counter = 10; //reset to start
  }
  else
  counter--;  //if not 0, keep on counting
  }
 
  columns++; //at each loop run, increment by one
  if (columns > 8)
  {
  columns = 0;  //if counter exceeds 8, reset it
  }
  PORTD = B00000000;  //turn latch low
  shiftOut(dataPin, clockPin, MSBFIRST, 1 << columns);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  pickNumber((now.minute() / m / 1) % 10);  //ones
  PORTD = B00001000;  //turn latch on->show screen
 
  PORTD = B00000000;  //turn latch low
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, 1 << columns);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  pickNumber((now.minute() / m / 10) % 10);  //tens
  PORTD = B00001000;  //turn latch on->show screen
 
  PORTD = B00000000;  //turn latch low
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, 1 << columns);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  pickNumber((now.hour() / m / 1) % 10);  //hundreds
  PORTD = B00001000;  //turn latch on->show screen
 
  PORTD = B00000000;  //turn latch low
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, B00000000);  //Send the data #2  (what columns to power)
  shiftOut(dataPin, clockPin, MSBFIRST, 1 << columns);  //Send the data #2  (what columns to power)
  pickNumber((now.hour() / m / 10) % 10);  //thousands
  PORTD = B00001000;  //turn latch on->show screen
}

void pickNumber( int count)  //pick numbers from array, at start of whole code
{
  switch (count)
  {
  case 1: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 8]); break;  //0
  case 2: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 16]); break;  //1
  case 3: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 24]); break;  //2
  case 4: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 32]); break;  //3
  case 5: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 40]); break;  //4
  case 6: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 48]); break;  //5
  case 7: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 56]); break;  //6
  case 8: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 64]); break;  //7
  case 9: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 72]); break;  //8
  case 10: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 80]); break;  //9
  default: shiftOut(dataPin, clockPin, LSBFIRST, numbers[columns + 0]); break;  //9
  }
}
 
also you said it worked with code from post 253, try that again , if you still get time, means we are not loosing power/time
At first, it showed 8585, then re-set time, wait while to check whether time will stay on memory, in the end it worked
now then, I tried again previous codes:
post #263, all empty
#264 FLICKERY seconds counter in place of 85 (wohoo)!
#273 same as 264
#274 whined about missing backbuffers, same error as earlier from that
#291, nothing on seconds place, empty except corners (some random flicker), but figures are mirror, see photo
and last, #293, flickery clock HH:MM format
IMG_1276.jpg
 
lol, thats rite, i did the mirror cause i forgot which side backbuffer[0] was.

Post 264 is where we cut out the interrupt, strange that it did that , and that it started working suddenly, try to reupload post 264, if it shows 85 then how long till it starts working? also let it run for about a minute or 2, also it should be scrolling,dont worry about flicker...yet!

also, just as fun, check out the first column in backbuffer from your last photo, you will notice that that is your seconds value in binary format, going from bottom to top, ob00101101 = 45 , so that photo was taken at 21:47:45 <--or 9:47pm!
 
Also,I noticed that switching digitalWrite() to port manipulation, scroll speed can be much better controlled by keeping refresh rate for interrupt constan (~2khz) but delay after commands keep screen readable!:
C:
#include "FontMap.h"

int dataPin = 2; // ic: 14, ser_in Define which pins will be used for the Shift Register control
int latchPin = 3; // ic:12 silkscreen numbers!
int clockPin = 4;


unsigned char displayPointer=0; // for interrupt use...
unsigned char buffer1[32]; // buffer for screen
unsigned char backbuffer[32]; // Spare screen for drawing on
unsigned char power[8]={128,64,32,16,8,4,2,1};

ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine, called every now and then, outside of loop
{
if(TIFR2) // Make sure its the timer interrupt.
{
setcolumn(displayPointer);  //column scanning
setdata(buffer1[displayPointer]);
PORTD = (1<<PORTD3);
PORTD = (0<<PORTD3);

//digitalWrite(latchPin ,HIGH);
//digitalWrite(latchPin , LOW ); // STORECLOCK

if(++displayPointer==32)
{ displayPointer = 0;
} // 32 LED row sections in total
}
TIFR2 = 0; // Clear timer 2 interrupt flag
}

void setcolumn(unsigned char col)  //loop that takes care of column scanning
{
signed char pos;
for (pos = 32;pos>-1;pos--)
{
if (col == pos)
{
  PORTD=(1<<PORTD2);
  //digitalWrite(dataPin ,HIGH);
  }
  else
  {
  PORTD=(0<<PORTD2);
  //digitalWrite(dataPin ,LOW);
  } // PIN1 DATA pin
  //PORTD=(1<<PORTD4);
  //PORTD=(0<<PORTD4);

digitalWrite(clockPin ,HIGH);
digitalWrite(clockPin ,LOW);
}
}



void setdata(unsigned char dat){
unsigned char pos;
for (pos = 0;pos<8;pos++)
{
if (dat & 128)
{
  dat-=128;
  PORTD=(1<<PORTD2);
  //digitalWrite(dataPin ,HIGH);
  }
  else
  {
  PORTD=(0<<PORTD2);
  }
  //digitalWrite(dataPin ,LOW);} // PIN1 DATA pin
dat = dat * 2;
digitalWrite(clockPin ,HIGH);
digitalWrite(clockPin ,LOW);
}}
void clr() //clear
{
int addr;
for(addr=0;addr<32;addr++) // Empty display buffer
backbuffer[addr]= 0;
}

void Blit() //transfers data between display buffer to screen buffer
{
int addr=0;
noInterrupts(); // disable all interrupts during setup
for(addr=0;addr < 32;addr ++)
{
buffer1[addr] = backbuffer[addr]; // put all data from display buffer
} // to screen buffer
interrupts(); // enable all interrupts
}

void pixel(signed char x,signed char y,int cond)
{
unsigned char pix,msk;
if(x<0 || y<0) return; // outside drawing limits negative
if(x>31 || y>7) return; // outside drawing limits positive
pix = power[y];
msk = backbuffer[x]; // get exsisting data


if(cond == 2)
pix ^= msk; // XOR data to screen
if (cond == 1)
{
pix = ~pix;
pix &= msk; // AND data to screen
}
if(cond == 0)
pix |= msk; // OR data to screen
backbuffer[x] = pix; // apply changes
}

void charput(unsigned char ch, signed char x,signed char y)
{
signed char x1, y1;
unsigned char disp;
unsigned char disp2;
for( x1=0;x1<8;x1++) // eight rows
{
disp = font[x1+(ch * 8)];
for (y1 = 0; y1<8; y1++) // eight pixels
{
disp2 = disp & power[y1];
if(disp2 > 0)
{
pixel(x+x1,y+y1,0); // OR the pixel to the display buffer
}
}

}
}

void strput(const char* ch, signed char x,signed char y)
{
int addr;
while (*ch )
{
charput(*ch++,x,y); // write a string to the display buffer
x+=7;
}
}



void setup() //setup runs once
{
signed char cntr;
noInterrupts(); // disable all interrupts during setup
DDRD = DDRD | B11111100; //port registers used to set pin directions
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 31; // compare match register 16MHz/256/2Hz -----------------------------------> delay time (lcd flicker/brightness)
TCCR1B |= (1 << WGM12); // CTC mode, free-running, clear on match
TCCR1B |= (0 << CS10); // 256 prescaler
TCCR1B |= (0 << CS11); // 256 prescaler
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts

} // End main

void loop() //just sitting here
{
signed char scrollctr;
unsigned char numCTR;

/*clr();
strput("WOOF",0,0);
Blit();
delay(5);*/

/*for (scrollctr=-8;scrollctr<9;scrollctr++)
{
clr();
strput("WOOF",0,scrollctr);
Blit();
delay(5);
}*/

for (scrollctr=32;scrollctr>-33;scrollctr--)
{
clr();
strput("WOOF",scrollctr,0);
Blit();
delay(100);
}

/*for (numCTR=0;numCTR<10;numCTR++){
for (scrollctr=-8;scrollctr<9;scrollctr++)
{
clr();
charput(numCTR,24,scrollctr);
Blit();
delay(5);
}
}*/



}
 
thats rite, the delay is used at the end of the for loop to hold off before writing new values to the buffers

also go with the port manipulation if it works faster, but really you want to control the flicker/refresh rate with OCR1A

soo cool!, glad is working , got worried for a sec there!
 
I ask this now when i remember: how to call character from fontmap if I create custom character and want to place it in place of some other letter? Like "HACK", but letter A is replaced?
 
I did plya with compare register but for some reason either it was way too fast or quite slow (tried values between 5-31250, as well as different register settings for cs10-12) I calculated 2khz for 60hz * 32, for POV illusion, so compare register value is =16mhz/(256*2khz)=~31, and indeed no flicker! As for code #264, can't see any scrolling.
I'm learning SO much here, thanks bunch! B
lol, thats rite, i did the mirror cause i forgot which side backbuffer[0] was.

Post 264 is where we cut out the interrupt, strange that it did that , and that it started working suddenly, try to reupload post 264, if it shows 85 then how long till it starts working? also let it run for about a minute or 2, also it should be scrolling,dont worry about flicker...yet!

also, just as fun, check out the first column in backbuffer from your last photo, you will notice that that is your seconds value in binary format, going from bottom to top, ob00101101 = 45 , so that photo was taken at 21:47:45 <--or 9:47pm!
^did we make binary clock by accident? :p
ut road is not end yet...
 
ohya, that ORCA1 may not work till we re-enable the interrupt, dont worry too much about that we can do final tweaks for that later.

I am worried though that you cant see scrolling, what about the binary values in the corner, they should be changing with time?
if not this could indicate we are still having problems reading from rtc

post298:
are several ways to do it, off the tip of tail ,
1)you could just write strput("hack",0,0); then use pixel() to adjust the individual pixels
2) change the fontmap position "A" to what you want (this would change output any time you used letter"A" in string)
3) use one of the free font positions ie 10-30 and draw special "A" there, then you would need to code like this:

strput("H CK",x,0); charput(10,x+8,y);

or

string s = ""; // declare the string variable (not exactly sure how, the declaration "string" is wrong)
s = "H" + (char)10 + "CK"; // char 10 being the ascii value of the font pos, again maybe written wrong too;
strput(s,0,0);--- something like that anyway , so many different languages i get them mixed up but that is the idea anyway of how to do.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top