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.

Can Oshonsoft use 'byref' in a FUNCTION? (Plus examples of CHAR MATCHING CODE)

camerart

Well-Known Member
Hi,
Here is a PROCEDURE from one of my programs:
'These extract characters from the raw RX buffer array to OSH string types
===================================
'AddChar: adds passed character to passed string at
'passed StrIx. Terminates result with 0 to keep it a string
Proc Addchar(ByRef str As String, ByRef StrIX As Byte, char As Byte)
str(StrIX) = char
StrIX = StrIX + 1
str(StrIX) = 0
End Proc
===================================
it uses 'byref'

I would like to know if 'byref' can also be used in an Oshonsoft FUNCTION please?
Camerart
 
It can surely be done, I have not found anything that cannot be done.
But you have to specify what the function does to be able to take a look at it.

I imagine you want to search for one string within another, if this is it, I already have a function that does it, then I can pass it to you so you can compare it with yours.
Hi D,
This project sends and receves messages from each other, plus each module receives GPS messages.
All of the messages start with a $.
The Function waits for the $ then interrupts, and reads the message/sentence, it checks the beginning of the message CHAR by CHAR, and if the required STRING CHARs (e,g GNRMC from a GPS) match the message CHARs it then carries on and reads the DATA in the message.
C.
 
DogFlu66 I think what you have done is marvelous. As you say, ANYTHING that can be done in assembly or C can be done in this IDE. Your routines are all excellent and work well, but using your library will be like using C to Cam. Its clear to me that you know ASM and C for these lil pic's, but some folk ( because they cannot immediately see code) will be working blind.

That said, I wish Vlad would have use signed / unsigned as its a chore to use without.
 
No No No! I don't use it commercially. I use C most of the time and as you know I have the full Proteus suite so I well covered. I bought the full Oshonsoft IDE and "extras" back when he started. At the time I only had C18 compiler and I used his stuff for tiny projects. I since bought XC8 and MicKroE's 32bit compiler.

Playing with Oshonsoft is just to "try" and help people such as yourself.
 
Here I leave it, along with some examples for those who want to play with it for a while.
I had to modify it because it was designed to work with pointers.
If it's not exactly what you need, at least it can serve as a starting point.

'Amicus18
'***********************************************************************
'Funciones String
'Funcion para comprobar que una cadena está contenida en otra.
'Pic18F26k22, OshonSoft Pic18 Basic Compiler v5.17
'By COS, 17/01/2024
'***********************************************************************
'Funciones para trabajar con cadenas mediante punteros
'***********************************************************************
#define CLOCK_FREQUENCY = 64 'Clock 64Mhz
#define STRING_MAX_LENGTH = 100
'#define SIMULATION_WAITMS_VALUE = 1
'***********************************************************************
'Include "_FuncionesPic18F26K22.bas"
'Include "_SetUpAmicus18.bas"
'Include "_STRING_LIBRARY.bas"
'Include "_FuncionesTmrBase.bas"
'Include "_FuncionesUartRingBuffer.bas"
'Include "_GENERAL_LIBRARY.bas"
'Include "_Funciones_Buffer_RS232.Bas"
'Include "_NMEA_LIBRARY.bas"
'***********************************************************************
main:
UART1_Init 115200
'UART2_Init 4800
WaitMs 1 '0

TRISA.2 = 0
Symbol Led = RA2
Led = 0

Dim String1 As String
Dim String2 As String
Dim String3 As String

While True
String1 = "!AIVDM,1,1,,B,4028jJivO;AJPOhKSNE4QQgP2<3j,0*5B"
String2 = "!AIVDO,1,1,,,100000?P?w<tSF0l4Q@>4?wv0000,0*53"
String3 = "!AIVDM"
UART_Write #_RevString("!AIVDO", String1), CrLf
UART_Write #_RevString("!AIVDO", String2), CrLf
UART_Write #_RevString(String3, String1), CrLf
UART_Write #_RevString(String3, String2), CrLf

UART_Write #_RevString("O", String2), CrLf
UART_Write #_RevString(String1, String1), CrLf

If _RevString(String3, String1) > 0 Then
UART_Write "Ok, hay concordancia"
Else
UART_Write "No hay conconrdancia"
Endif

While True
Wend
Wend

End
'*********************************************************************
'Look for the first string inside the second.
'Returns 0 does not exist or greater than zero (position of the first character) if it exists.
'This function considers the first character as 1 from left to right.
Function _RevString(_String1 As String, _string2 As String) As Word
Symbol _Return = _RevString
Dim _index1 As Word
Dim _index2 As Word
Dim _counter As Byte
_index1 = 0
_index2 = 0
_Return = 0
_counter = 0
While True 'Iterates through the strings, infinite loop
If _string2(_index2) = _String1(_index1) Then 'if characters match
If _String1(_index1 +1) = 0 Then 'There are no more characters or end of string
_Return = _index2 +1 - _counter 'First character position
Exit
Endif
_counter++ 'Total compared characters of the second string
_index1++ 'Points to the next element string1
Else 'If the characters are different
_Return = 0 'It is marked that the string is not contained
_counter = 0 'The number of compared characters is set to zero
If _string2(_index2) = 0 Or _String1(_index1) = 0 Then Exit 'If any string is finished, exit
_index1 = 0 'The first element of string 1 is pointed again
Endif
_index2++ 'Next element in the string
Wend
End Function

Note: removed some junk code that was carried over from the previous version and corrected bug.
 

Attachments

  • _RevString Function.bas
    3.1 KB · Views: 151
Last edited:
Here I leave it, along with some examples for those who want to play with it for a while.
I had to modify it because it was designed to work with pointers.
If it's not exactly what you need, at least it can serve as a starting point.

'Amicus18
'************************************************* **********************
'String functions
'Function to check that one string is contained in another.
'Pic18F26k22, OshonSoft Pic18 Basic Compiler v5.17
'By COS, 01/17/2024
'************************************************* **********************
#define CLOCK_FREQUENCY = 64 'Clock 64Mhz
#define STRING_MAX_LENGTH = 100
'#define SIMULATION_WAITMS_VALUE = 1
'************************************************* **********************
'Include "_FuncionesPic18F26K22.bas"
'Include "_SetUpAmicus18.bas"
'Include "_STRING_LIBRARY.bas"
'Include "_FuncionesTmrBase.bas"
'Include "_FuncionesUartRingBuffer.bas"
'Include "_GENERAL_LIBRARY.bas"
'Include "_Functions_Buffer_RS232.Bas"
'Include "_NMEA_LIBRARY.bas"
'************************************************* **********************
main:
UART1_Init 115200
'UART2_Init 4800
WaitMs 1 '0

TRISA.2 = 0
Symbol Led = RA2
Led = 0

'UART1_Write "Ready", CrLf
'UART2_Write "Ready", CrLf

Dim String1 As String
Dim String2 As String
Dim String3 As String

String1 = "!AIVDM,1,1,,B,4028jJivO;AJPOhKSNE4QQgP2<3j,0*5B"
String2 = "!AIVDO,1,1,,,100000?P?w<tSF0l4Q@>4?wv0000,0*53"
String3 = "!AIVDM"
UART_Write #_RevString("!AIVDO", String1), CrLf 'No
UART_Write #_RevString("!AIVDO", String2), CrLf 'Ok
UART_Write #_RevString(String3, String1), CrLf 'OK
UART_Write #_RevString(String3, String2), CrLf 'No
UART_Write #_RevString(String1, String3), CrLf 'No
UART_Write #_RevString(String3, String3), CrLf 'Ok

If _RevString(String3, String1) > 0 Then
UART_Write "Ok, there is a match"
Else
UART_Write "No match"
Endif

While True
Wend

End
'************************************************* ********************
'Look for the first string inside the second.
'Returns 0 does not exist or position of the first character if it exists.
'This function considers the first character as 1 from left to right.
Function _RevString(_String1 As String, _String2 As String) As Word
Symbol _Return = _RevString
Dim _index1 As Word
Dim _index2 As Word
Dim _index2img As Word
Dim _char2 As Byte
Dim _char1 As Byte
Dim _counter As Byte
_index2 = 0 'First element of the string
_index2img = 0 'First element of string (image)
_index1 = 0 'First element of the string
_counter = 0
_Return = 0 'Error
While True 'Iterates through the strings
_char2 = _String2(_index2) 'Assign the position value
_char1 = _String1(_index1) 'Assign the position value
_index2++ 'Points to the next element in string 2
If _String2(_index2 -1) = _String1(_index1) Then 'If characters are equal
If _String1(_index2) = 0 Then _Return = ((_index2) - _index2img) - _counter 'First character position
If _String1(_index2) = 0 Then Exit 'There are no more characters, exit
_counter++ 'Total compared characters of the second string
_index1++ 'Points to the next element string1
Endif
If _String2(_index2 -1) <> _String1(_index1 - 1) Then 'The characters are not equal
_counter = 0 'The number of compared characters is set to zero
If _String2(_index2 -1) = 0 Or _String1(_index1 -1) = 0 Then Exit 'If any string is finished, it exits.
_index1 = 0 'The first element of string 1 is pointed again
_Return = 0 'It is marked that the string is not contained
Endif
Wend
End Function
Hi D,
It will take me a while to check it, thank you.
C.
 
It is just out of habit, to emphasize that they are user functions. Well, there is also one more reason, and that is that an update to a compiler that I used had some conflicts between local variables and global variables when they are named with the same name. In any case, there are many programmers who have that habit.
 
Hi,
My mate has written CODE, shown here:
This seems to work, so I'll try adding it into the main program, and report back.
C
Code:
'18F4431 32MHz XTL PCB8 BASE_SLAVE MATCH SHORT D 210123 1501

Define CONFIG1L = 0x00
Define CONFIG1H = 0x06  '8mHz x4 =32
Define CONFIG2L = 0x0c
Define CONFIG2H = 0x20
Define CONFIG3L = 0x04
Define CONFIG3H = 0x80
Define CONFIG4L = 0x80  'Set for HVP
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

'OSH config
Define CLOCK_FREQUENCY = 32
Define SINGLE_DECIMAL_PLACES = 2
Define STRING_MAX_LENGTH = 20
Define SEROUT_DELAYUS = 1000

'**********************************************************************
'*                                                                    *
'*      Register configuration                                        *
'*                                                                    *
'**********************************************************************

'------------- TARGET SETUP  ------------------
'Const PR2set = 125  'use this for the live board: gets 5ms per interrupt
Const PR2set = 2  'use this for fast timing to speed up simulator
Define SIMULATION_WAITMS_VALUE = 1  'Comment in for SIM out for PIC

'**********************************************************************
'*                                                                    *
'*          IO Port configuration                                     *
'*                                                                    *
'**********************************************************************
'IN or OUT
Const TRISAinit = %11011000  '7=OSC, 6=OSC, 4=QEIB 3=QEIA 2=TEMP SEROUT
Const TRISBinit = %00000000
Const TRISCinit = %11000100  '7=1-RX, 6=1-slave4431_cs, 2=MOSI'RX<<<<<<<<<<<<<<<<<<<<<
Const TRISDinit = %00100000  '6=led, 7=led, 5=synch'D2=MOSI ??£
Const TRISEinit = %00000000  '2=TEST INDICATOR, 0= S2M_BUF_FULL

TRISA = TRISAinit
TRISB = TRISBinit
TRISC = TRISCinit
TRISD = TRISDinit
TRISE = TRISEinit

'SET BITS ON/OFF before TRIS!
Const LATAinit = %00000000  'ON/OFF
Const LATBinit = %00000000
Const LATCinit = %00000000
Const LATDinit = %00000000
Const LATEinit = %00000000  'POSS MCLR RE3

Symbol yled = PORTD.6
Symbol rled = PORTD.7

'START UP LEDS
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
yled = 1
WaitMs 1000
yled = 0
WaitMs 1000

Dim str_to_match As String  ' the string to match
Dim gnrmc_str As String  ' the string used for test purposes
Dim position As Byte  ' the position in the main string to start from

main_loop:  '/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

Toggle rled
'set the string  to be checked (in this case called gnrmc_str)
gnrmc_str = "$GNRMCY.00A3723."  'edit the gnrmc bit to produce an invalid string while testing

str_to_match = "GNRMC"  ' the string to be checked for

position = 1  ' the starting position of the long string (in this case gnmrc_str

'check For the id String
'This funtion is called "match"
'match uses 3 inputs. The first is the string to be tested. I have used gnrmc_str but it can be any valid string variable
'The second input is the position to start checking from. in this case it is the numbber 1
'(arrays start at 0 so the second character in the array is 1)
'The third input is the string of characters to be checked against. I have used gnrmc in
'this example but it could be robi, ROBI, bori, BORI etc

If match(gnrmc_str, position, str_to_match) = 1 Then
    Serout PORTB.0, 9600, "GOOD  ", gnrmc_str(0), gnrmc_str(1), gnrmc_str(2), gnrmc_str(3), gnrmc_str(4), gnrmc_str(5), gnrmc_str(6), gnrmc_str(7), CrLf
Else
    Serout PORTB.0, 9600, "BAD  ", gnrmc_str(0), gnrmc_str(1), gnrmc_str(2), gnrmc_str(3), gnrmc_str(4), gnrmc_str(5), gnrmc_str(6), gnrmc_str(7), CrLf
Endif

Break  ' remove this line when finished testing

Goto main_loop

End                                               

'match function
Function match(ByVal input_str As String, ByVal str_ix As Byte, ByVal match_str As String) As Bit  ' these variable names can be changes as wished

    Dim match_str_len As Byte  'the number of characters to be match checked
    Dim position_counter As Byte  ' the index pointer for the character to be checked
    Dim position_ix As Byte  'the position to start checking from
    Dim shortstring As Byte  ' needed to make the loop work
    Dim shortposition As Byte  'the current position in the short string
    
    position_ix = str_ix + match_str_len  'the position to start checking from
    
    match_str_len = Len(match_str)  ' find the length of the test string to be compared
    
    For position_counter = str_ix To match_str_len
    Break  '<<<<<<<<<<<<<<
        shortstring = position_counter - match_str_len
        If input_str(position_counter) = match_str(shortposition) Then
            match = 1  ' set the match indicator to valid
            shortposition = shortposition + 1  ' increment the short string position counter
        Else
            match = 0  'set the match indicator To Not valid
            Exit  ' jump out of the loop
        Endif
    Next position_counter

End Function
 

Attachments

  • MATCH.jpg
    MATCH.jpg
    352.7 KB · Views: 141
Last edited:
The integration is the same, what happens with yours is that it practically only serves that use.
The one I have proposed is suitable for more uses. Locates the string in any part of the other string and returns the position of the first character where they match. This return value can be supplied to another function to do something else on the string, those who have more experience with strings will understand the possibilities that the latter offers.

'18F4431 32MHz XTL PCB8 BASE_SLAVE match 2 strings E 180123 1001

'*************************************************************************
'* *
'* slave mcu initialise routines brought back *
'* Needed here as Oshonsoft placed the include code *
'* at the end of the compiled program *
'* *
'*************************************************************************

Define CONFIG1L = 0x00
Define CONFIG1H = 0x06 '8mHz x4 =32
Define CONFIG2L = 0x0c
Define CONFIG2H = 0x20
Define CONFIG3L = 0x04
Define CONFIG3H = 0x80
Define CONFIG4L = 0x80 'Set for HVP
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

'OSH config
Define CLOCK_FREQUENCY = 32
Define SINGLE_DECIMAL_PLACES = 2
Define STRING_MAX_LENGTH = 20
Define SEROUT_DELAYUS = 1000

'**********************************************************************
'* *
'* Register configuration *
'* *
'**********************************************************************

'------------- TARGET SETUP ------------------
'Const PR2set = 125 'use this for the live board: gets 5ms per interrupt
Const PR2set = 2 'use this for fast timing to speed up simulator
Define SIMULATION_WAITMS_VALUE = 1 'Comment in for SIM out for PIC

'**********************************************************************
'* *
'* IO Port configuration *
'* *
'**********************************************************************
'IN or OUT
Const TRISAinit = %11011000 '7=OSC, 6=OSC, 4=QEIB 3=QEIA 2=TEMP SEROUT
Const TRISBinit = %00000000
Const TRISCinit = %11000100 '7=1-RX, 6=1-slave4431_cs, 2=MOSI'RX<<<<<<<<<<<<<<<<<<<<<
Const TRISDinit = %00100000 '6=led, 7=led, 5=synch'D2=MOSI ??£
Const TRISEinit = %00000000 '2=TEST INDICATOR, 0= S2M_BUF_FULL

TRISA = TRISAinit
TRISB = TRISBinit
TRISC = TRISCinit
TRISD = TRISDinit
TRISE = TRISEinit

'SET BITS ON/OFF before TRIS!
Const LATAinit = %00000000 'ON/OFF
Const LATBinit = %00000000
Const LATCinit = %00000000
Const LATDinit = %00000000
Const LATEinit = %00000000 'POSS MCLR RE3

Symbol yled = PORTD.6
Symbol rled = PORTD.7


'START UP LEDS
rled = 1
WaitMs 1000
rled = 0
WaitMs 1000
yled = 1
WaitMs 1000
yled = 0
WaitMs 1000


Dim to_match_str As String ' the string to match
Dim gnrmc_str As String ' the string used for test purposes
Dim position As Byte ' the position in the main string to start from


main_loop: '/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

Toggle rled


' set the string to be checked (in this case called gnrmc_str)
gnrmc_str = "$GNRMC0.00A3723." 'edit the gnrmc bit to produce an invalid string while testing

to_match_str = "GNRMC" ' the string to be checked for

position = 1 ' the starting position of the long string (in this case gnmrc_str

'check For the id String
' I have called this funtion "match" - you can call it anything you like as long as the name is consistent

' match uses 3 inputs. The first is the string to be tested. I have used gnrmc_str but it can be any valid string variable
' The second input is the position to start checking from. in this case it is the numbber 1
' (arrays start at 0 so the second character in the array is 1)
' The third input is the string of characters to be checked against. I hane used gnrmc in
' this example but it could be robi, ROBI, bori, BORI etc


'Call match(gnrmc_str, position, to_match_str)



'If match(gnrmc_str, position, to_match_str) = 1 Then
If _RevString(to_match_str, gnrmc_str) > 0 Then

Serout PORTB.4, 9600, "GOOD", CrLf
' if OK go to good string found
Else

Serout PORTB.4, 9600, "BAD", CrLf
'if not ok goto bad string found

Endif
While True 'Break ' remove this line when finished testing
Wend

Goto main_loop

End
'*********************************************************************
'Look for the first string inside the second.
'Returns 0 does not exist or greater than zero (position of the first character) if it exists.
'This function considers the first character as 1 from left to right.
Function _RevString(_String1 As String, _string2 As String) As Word
Symbol _Return = _RevString
Dim _index1 As Word
Dim _index2 As Word
Dim _counter As Byte
_index1 = 0
_index2 = 0
_Return = 0
_counter = 0
While True 'Iterates through the strings, infinite loop
If _string2(_index2) = _String1(_index1) Then 'if characters match
If _String1(_index1 +1) = 0 Then 'There are no more characters or end of string
_Return = _index2 +1 - _counter 'First character position
Exit
Endif
_counter++ 'Total compared characters of the second string
_index1++ 'Points to the next element string1
Else 'If the characters are different
_Return = 0 'It is marked that the string is not contained
_counter = 0 'The number of compared characters is set to zero
If _string2(_index2) = 0 Or _String1(_index1) = 0 Then Exit 'If any string is finished, exit
_index1 = 0 'The first element of string 1 is pointed again
Endif
_index2++ 'Next element in the string
Wend
End Function
'*********************************************************************************
'the corresponding match function
Function match(ByVal input_str As String, ByVal str_ix As Byte, ByVal match_str As String) As Bit ' these variable names can be changes as wished

Dim match_str_len As Byte 'the number of characters to be match checked
Dim position_counter As Byte ' the index pointer for the character to be checked
Dim position_ix As Byte 'the position to start checking from
Dim shortstring As Byte ' needed to make the loop work
Dim shortposition As Byte 'the current position in the short string


position_ix = str_ix + match_str_len 'the position to start checking from


match_str_len = Len(match_str) ' find the length of the test string to be compared

For position_counter = str_ix To match_str_len
shortstring = position_counter - match_str_len


If input_str(position_counter) = match_str(shortposition) Then
match = 1 ' set the match indicator to valid
shortposition = shortposition + 1 ' increment the short string position counter
Else
match = 0 'set the match indicator To Not valid
Exit ' jump out of the loop
Endif

Next position_counter

End Function
 
The integration is the same, what happens with yours is that it practically only serves that use.
The one I have proposed is suitable for more uses. Locates the string in any part of the other string and returns the position of the first character where they match. This return value can be supplied to another function to do something else on the string, those who have more experience with strings will understand the possibilities that the latter offers.
Hi D,
It now compiles, and works ok.
I'll show my mate who will understand the differences better, and use the most suitable and understandable for me. It will take me many simulations, to recognise the value, I'm sure.
Many thanks.
C
 
What matters is that the function does its job well.

In any case, all this is interesting because I have to modify software to make some discriminations of some GPS and AIS sentences.
 
What matters is that the function does its job well.

In any case, all this is interesting because I have to modify software to make some discriminations of some GPS and AIS sentences.
Hi D,
Most of what I do programming wise, involves using CODE that someone else has written for me, as that is the weakest link with the project I'm working on.

I hope no one is affended by not choosing the CODE they have written. I'll let my mate choose which of the 3x CODES I have, but from what I understand yours maybe the most useful perhaps later ?

One of the reasons for this discriminating CODE is for GPS sentences. I'm not sure if this is what you refer to but, some of my GPS modules can be programmed to discriminate out the unwanted sentences, so only used as back up. Let me know if this is your reason, and if so perhaps I can help you to program yours?
C
 
Not at all, you can choose the code that suits you best, just participating a little is enough.

Changing the subject; For a couple of years now I have been manufacturing an automation (among some others) that is nothing more than a data processor for NMEA frames from different sources. I am dedicated to naval issues. When the same position enters (provided by different gps or ais receivers) generated by different equipment, a pitching of the bow of the boat occurs, and now I want to go up to the next level, which is to leave one entry as the main one, and the others, If a device also provides position among its different data frames, then filter it and eliminate it. But this would be work, and here I am as a hobby. In fact, what I mean is that the problems you have are the same ones that I have in my projects. I am currently building some libraries to work with NMEA data, as problems arise as the library grows in functions.
 
DogFlu66 I think what you have done is marvelous. As you say, ANYTHING that can be done in assembly or C can be done in this IDE. Your routines are all excellent and work well, but using your library will be like using C to Cam. Its clear to me that you know ASM and C for these lil pic's, but some folk ( because they cannot immediately see code) will be working blind.

That said, I wish Vlad would have use signed / unsigned as its a chore to use without.
You were the only one who noticed.
 
The integration is the same, what happens with yours is that it practically only serves that use.
The one I have proposed is suitable for more uses. Locates the string in any part of the other string and returns the position of the first character where they match. This return value can be supplied to another function to do something else on the string, those who have more experience with strings will understand the possibilities that the latter offers.
Hi D,
I'll need to check all 3x CODEs, so I understand better how to compare them.
From what I understand, yours checks the whole sentences, where the other 2x only check the beginning.
If this correct, then the other 2x seem faster, as once e,g, $GNRMC has been checked, they move to the next sentence.
I think I can see that yours could 'say' compare values for wildly different results, and eliminate them, or average them, if I'm correct.
C.
 
If it finds the match it exits, if not it runs through the entire chain looking for it.

In this specific case, yours seems like the best choice.
 
Last edited:

New Articles From Microcontroller Tips

Back
Top