Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 01:42 05 Feb 2023 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : uM2: 0.9Msps dual channel oscilloscope

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 6828
Posted: 12:51am 26 Oct 2015
Copy link to clipboard 
Print this post

Here is a version of the oscilloscope code to run on a 28 or 44-pin Micromite with a ILI9341 SPI connected display. The display must be configured in landscape or reverse landscape mode.




The uM2 can't quite make the speed of the MM+, not because of the ADC, but because the code can't store the data fast enough without missing ADC inputs. In fact the MX170 ADC seems less noisy than the MX470 so the trace is cleaner.

All the usage is as per the original thread but I kept this one separate as there may be a bigger audience for the 28 and 44-pin chips


CPU 48
option explicit
option default none
dim integer i,j=0,toggle,channel1buff(320),channel2buff(320),microsecondsperdivision=50
dim integer y1pos=50, y2pos=10, c1mvperdivision=200, c2mvperdivision=1000, c1trig=2, c2trig=0
' 28-pin
'setpin 2,ain ' Channel 1
'setpin 23,ain 'Channel 2
' 44-pin
setpin 19,ain 'Channel 1
setpin 10,ain 'Channel 2

colour rgb(white),rgb(blue)
cls
font 1
'
' Starting code for a Micromite based oscilloscope - needs a GUI (volunteers?)
' Runs on uM2 with a 320x240 ILI9341display
' For 28-pin chip uses pin 2 for channel 1 and pin 23 for channel 2
' For 44-pin chip uses pin 19 for channel 1 and pin 10 for channel 2
' Maximum conversion rate is approximately 0.9 Megasamples / second on each channel at 10-bits resolution
' The CFunction will automatically compensate for CPU speed but obviously the higher the speed the better
'
'
' Parameters for call to CFunction "scope"
'
' Returns the scaling factor used for the display or a large number if triggering is enabled and has timed out
' A scale factor of 1 means every x-coordinate has a unique value
'
' microsecondsperdivision: specifies the number of microseconds to scale the display for each graticle line
' note: durations less than 100uSec will use scaling. i.e.every other x-coordinate will be missed if scale = 2
' toggle: initialised to 0, used by the CFunction to control its double buffering
' channel1buff: buffer used for channel 1, used as 2 buffers of 320 x 32-bit words
' channel2buff: buffer used for channel 2, used as 2 buffers of 320 x 32-bit words
' y-position for channel 1: specifies the screen position of the zero volts on channel 1 in rows from the bottom of the display
' use any negative number to turn off the channel
' y-position for channel 2: specifies the screen position of the zero volts on channel 2 in rows from the bottom of the display
' use any negative number to turn off the channel
' trigger for channel 1
' specifies the ADC reading and direction to trigger the trace for channel 1 (-1023 to +1023)
' a zero value means triggering is disabled for this channel
' a positive value triggers when the ADC reading passes the trigger value in a positive going sense
' a negative value triggers when the ADC reading passes the absolute trigger value in a negative going sense
' trigger for channel 2
' specifies the ADC reading and direction to trigger the trace for channel 2 (-1023 to +1023)
' a zero value means triggering is disabled for this channel
' a positive value triggers when the ADC reading passes the trigger value in a positive going sense
' a negative value triggers when the ADC reading passes the absolute trigger value in a negative going sense
' NB: if both trigger levels are set to zero the scope will free run
' millivolts per division for channel 1 based on VDD=3.3V
' millivolts per division for channel 2 based on VDD=3.3V
'

do
i= scopeuM(microsecondsperdivision,toggle,channel1buff(),channel2buff(),y1pos, y2pos, c1trig, c2trig ,c1mvperdivision,c2mvperdivision)
if i>1 and i<1024 and j=0 then
print "Warning reduced resolution x"+str$(i)
j=1
endif
if i>1024 then
PRINT "No trigger"
endif
text 2,2,"T:"+str$(microsecondsperdivision)+"us/div"
if y1pos>=0 then text 100,2,"C1:"+str$(c1mvperdivision)+"mv/div"
if y2pos>=0 then text 200,2,"C2:"+str$(c2mvperdivision)+"mv/div"
loop
end
'
CFunction scopeuM
00000000
27BDFF90 AFBF006C AFBE0068 AFB70064 AFB60060 AFB5005C AFB40058 AFB30054
AFB20050 AFB1004C AFB00048 8FA2008C 8C830000 3C049D00 8C840000 8C880000
240403E8 0104001B 008001F4 8FA40090 8C840000 AFA40018 8FA40094 8C840000
AFA4001C 8CA90000 8CA80004 01282025 10800006 00005012 24F20500 24D30500
AFA60034 10000007 AFA70030 24E40500 AFA40030 24D00500 AFB00034 00C09821
00E09021 00094827 ACA90000 00084027 ACA80004 706A3002 24050001 3404FA00
00C4001B 008001F4 00003812 00001812 00042042 2CE70040 14E0FFF9 00052840
00052842 AFA50020 8FA40088 8C870000 14E00005 2404FFFF 8C460000 0006200B
10000006 00803021 18E00004 00003021 7C073E20 10000004 2416FFFF 00073823
7C073E20 0000B021 8C480000 19000003 2417FFFF 10000004 7C084620 00084023
7C084620 0000B821 3C04BF88 3C021000 AC821064 3C02BF88 AC401068 34058000
3C09BF81 AD259004 3C0A0C00 3C09BF81 AD2A9040 3C09BF88 AD206118 3C09BF88
AD206108 3C0AFFFF 3C09BF81 AD2A9050 240A0200 3C09BF81 AD2A9020 240A0007
3C09BF81 AD2A9010 340A80E4 3C09BF81 AD2A9000 3C09BF81 AD259008 3C050008
AC851064 AC401068 3C04BF81 8C829010 7C4201C0 1040FFFD 3C02BF81 8C429070
3C02BF81 8C429080 3C04BF81 8C829010 30420080 1440FFFD 3C02BF81 8C4290F0
3C02BF81 8C429100 3C02BF81 8C429070 24420001 AE620000 3C02BF81 8C429080
24420001 AE420000 00001021 40824800 24020140 8FA40020 0044001B 008001F4
0000F812 0000F012 00002821 00002021 3C090007 3529A120 3C02BF81 3C0EBF81
3C0DBF81 00006021 0100C021 240FFFFF 02EF5026 AFAA0024 00E0A821 02CF8026
AFB00028 3C0BBF81 10000076 3C0ABF81 8C599010 7F3901C0 1320FFFD 00000000
40194800 0323001B 006001F4 00008812 0091C82B 13200031 00000000 8DD99070
8DB09080 10C00007 00048880 0271A021 AE990000 02518821 AE300000 10000027
24840001 10E0000F 00000000 8E740000 AFB40040 0287A02A 12800184 8FB40040
0327882B 01E0A021 00D6A00A 1000017E 0291300A 16200003 00068827 8FB40028
0234300B 51000011 AE790000 8E510000 0228A02A 5280017B 0311882A 0208A02B
56800178 0311882A 0006A027 10000174 0297300B 56200005 AE790000 00068827
8FB40024 0234300B AE790000 AE500000 0180C821 40994800 24A50001 8C599010
33390080 1720FFFD 00000000 40194800 0323001B 006001F4 00008012 0090C82B
13200031 009EC82B 8D7990F0 8D509100 10C00007 00048880 0271A021 AE990000
02518821 AE300000 10000026 24840001 10E0000E 00000000 8E710000 0227A02A
12800154 AFB10040 0327A02B 01E08821 00D6880A 1000014F 0234300A 16200003
00068827 8FB40028 0234300B 51000011 AE790000 8E510000 0228A02A 5280014C
0311882A 0208A02B 56800149 0311882A 0006A027 10000145 0297300B 56200005
AE790000 00068827 8FB40024 0234300B AE790000 AE500000 0180C821 40994800
24A50001 009EC82B 13200003 00A9C82B 1720FF87 00000000 40024800 3C04BF88
3C021000 AC821064 3C03BF88 AC601068 34068000 3C02BF81 AC469004 3C020008
AC821064 AC621068 2442A120 00A2282B 10A000CF 3C020007 2FFF0002 17E000CE
8FB40030 8FB50034 8FB60020 0000B821 241F0001 3C109D00 241100EF 3C0200FF
3442FFFF AFA20024 AFBE0028 03E0F021 8FA20080 8C430000 0460002A 240A0001
8EA20000 000220C0 00022940 00A42023 00821021 00022100 00821023 00022140
00441021 8FA40018 0044001B 008001F4 00002812 24A50040 000529C2 02252823
8EA20004 000220C0 00023140 00C42023 00821021 00022100 00821023 00022140
00441021 8FA40018 0044001B 008001F4 00003812 24E70040 000739C2 02273823
AFAA0010 240200FF AFA20014 8E020050 02E02021 00A32823 02C03021 0040F809
00E33823 8FA20084 8C430000 0460002A 240A0001 8E820000 000220C0 00022940
00A42023 00821021 00022100 00821023 00022140 00441021 8FA4001C 0044001B
008001F4 00002812 24A50040 000529C2 02252823 8E820004 000220C0 00023140
00C42023 00821021 00022100 00821023 00022140 00441021 8FA4001C 0044001B
008001F4 00003812 24E70040 000739C2 02273823 AFAA0010 240200FF AFA20014
8E020050 02E02021 00A32823 02C03021 0040F809 00E33823 8FA20080 8C430000
0460002A 240A0001 8E620000 000220C0 00022940 00A42023 00821021 00022100
00821023 00022140 00441021 8FA40018 0044001B 008001F4 00002812 24A50040
000529C2 02252823 8E620004 000220C0 00023140 00C42023 00821021 00022100
00821023 00022140 00441021 8FA40018 0044001B 008001F4 00003812 24E70040
000739C2 02273823 AFAA0010 8FA20024 AFA20014 8E020050 02E02021 00A32823
02C03021 0040F809 00E33823 8FA40084 8C830000 0460002A 240A0001 8E420000
000220C0 00022940 00A42023 00821021 00022100 00821023 00022140 00441021
8FA4001C 0044001B 008001F4 00002812 24A50040 000529C2 02252823 8E420004
000220C0 00023140 00C42023 00821021 00022100 00821023 00022140 00441021
8FA4001C 0044001B 008001F4 00003812 24E70040 000739C2 02273823 AFAA0010
8FA20024 AFA20014 8E020050 02E02021 00A32823 02C03021 0040F809 00E33823
27DE0001 26520004 8FA40020 02E4B821 02C4B021 26730004 26940004 8FAA0028
155EFF43 26B50004 10000004 00008021 3442A120 AFA20020 00008021 3C119D00
24120001 3C130080 36738080 241400F0 AFB20010 AFB30014 8E220050 00002021
02002821 2406013F 0040F809 02003821 2610001E 5614FFF7 AFB20010 00008021
3C119D00 24120001 3C130080 36738080 24140140 AFB20010 AFB30014 8E220050
02002021 00002821 02003021 0040F809 240700EF 26100020 5614FFF7 AFB20010
3C109D00 24110001 AFB10010 3C120080 36528080 AFB20014 8E020050 00002021
240500EF 2406013F 0040F809 240700EF AFB10010 AFB20014 8E020050 2404013F
00002821 2406013F 0040F809 240700EF 8FA20020 00001821 8FBF006C 8FBE0068
8FB70064 8FB60060 8FB5005C 8FB40058 8FB30054 8FB20050 8FB1004C 8FB00048
03E00008 27BD0070 8FB40040 02B4882A 1620FE80 00F9882B 1000FE82 00000000
0311882A 1620FE8B 0110882B 1000FE8F AE790000 8FB40040 02B4882A 1620FEAF
00F9882B 1000FEB1 00000000 0311882A 1620FEBA 0110882B 1000FEBE AE790000
End CFunction

'

 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1071
Posted: 04:13am 26 Oct 2015
Copy link to clipboard 
Print this post

Hi Peter,

this is absolutely fun-tastic! (You made my day!)
I did not dare to ask you about a uM2 version.

For a first experience I used this slightly modified code.

CPU 48
Option explicit
Option default none
Dim integer i,j=0,toggle,channel1buff(320),channel2buff(320)
Dim integer microsecondsperdivision=10
Dim integer y1pos=-50, y2pos=0
Dim integer c1mvperdivision=1000, c2mvperdivision=100
Dim integer c1trig=0, c2trig=100
' 0.9Msps dual channel oscilloscope
' 28-pin
SetPin 2,ain ' Channel 1
SetPin 23,ain 'Channel 2
' 44-pin
'SetPin 19,ain 'Channel 1
'SetPin 10,ain 'Channel 2

Colour RGB(white),RGB(blue)
CLS
Font 2

Do
i= scopeuM(microsecondsperdivision,toggle,channel1buff(),channel2buff(),y1pos
If i>1 And i<1024 And j=0 Then
Print "Warning reduced resolution x"+Str$(i)
j=1
EndIf
If i>1024 Then
Print "No trigger"
EndIf
Text 2,1,"T:"+Str$(microsecondsperdivision)+"us/div"
If y1pos>=0 Then Text 100,2,"C1:"+Str$(c1mvperdivision)+"mv/div"
If y2pos>=0 Then Text 220,2,"C2:"+Str$(c2mvperdivision)+"mv/div"
Loop
End


and your CFunction scopeuM of course.


With a 25KHz signal on PIN23 (only) I got this pictures, very nice!





But this is just a first step ...

May I ask you about the C source? Perhaps I'm able to learn something.

Thanks a lot!

Regards
Michael


 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 6828
Posted: 05:23am 26 Oct 2015
Copy link to clipboard 
Print this post

  Quote  May I ask you about the C source?


long long scopeuM(long long *microsecondsperdivision, long long *toggle, long *channel0, long *channel1, long *c0pos, long *c1pos, long *c0trig, long *c1trig, long *mpv0, long *mpv1){
unsigned int count=320,loop=0,k=239,div=64000,mps=*microsecondsperdivision,scale=1;
unsigned int cpu=CurrentCpuSpeed/1000,s0=*mpv0,s1=*mpv1;
unsigned int current_ticks=0,ticktest=0,triggered=0,currentc0,currentc1, triggertimeout=0;
short c0dir=0,c1dir=0,c0lev=0,c1lev=0;
long *c1;
long *c0;
long *c1last;
long *c0last;
if(*toggle){ //decide which buffer to use
c1=channel1+320;
c0=channel0+320;
c1last=channel1;
c0last=channel0;
} else {
c1=channel1;
c0=channel0;
c1last=channel1+320;
c0last=channel0+320;
}
*toggle=~(*toggle); //swap buffers for next time

while(ticktest<64){ //implement scaling if display resolution too high for the ADC
ticktest=cpu*mps/div;
div/=2;
scale*=2;
}
scale/=2;

if (*c0trig==0 && *c1trig==0)triggered=~triggered; //no trigger specified so free run
if(*c0trig>0){
c0lev=*c0trig;
c0dir=~c0dir;
} else {
c0lev=-(*c0trig);
}
if(*c1trig>0){
c1lev=*c1trig;
c1dir=~c1dir;
} else {
c1lev=-(*c1trig);
}

// configure and enable the ADC
CloseADC10();// ensure the ADC is off before setting the configuration
// define setup parameters for OpenADC10
#define PARAM3A ADC_CONV_CLK_SYSTEM | ADC_SAMPLE_TIME_2
#define maxwait 500000 //wait for 500,000 samples before timing out if no trigger
// configure to sample AN0 & AN12
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0 |ADC_CH0_NEG_SAMPLEB_NVREF | ADC_CH0_POS_SAMPLEB_AN12);
// configure ADC and enable it
OpenADC10( PARAM1, PARAM2, PARAM3A, PARAM4, PARAM5 );
// Now enable the ADC logic
EnableADC10();
// the results of the conversions are available in channel0 and channel1
mT4IntEnable(0); // disable clock interrupt

while (~ReadActiveBufferADC10() & 1); //wait for first conversion to complete
currentc0=ReadADC10(0);
currentc1=ReadADC10(1);
while (ReadActiveBufferADC10()); //wait for second conversion to complete
currentc0=ReadADC10(8);
currentc1=ReadADC10(9);
c0[0]=(ReadADC10(0)+1);
c1[0]=(ReadADC10(1)+1);

asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
while(loop<count/scale && triggertimeout<maxwait)
{
while (~ReadActiveBufferADC10() & 1); //wait for conversion to complete

asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
if(current_ticks/ticktest>loop){ //save the ADC reading if I'm into the next time period
currentc0=(ReadADC10(0));
currentc1=(ReadADC10(1));
if(triggered){
c0[loop] = currentc0;
c1[loop++] = currentc1;
} else{
if((c0lev) && (c0[0]<c0lev) && (currentc0>=c0lev) && (c0dir)) triggered=~triggered;
if((c0lev) && (c0[0]>c0lev) && (currentc0<=c0lev) && (~(c0dir))) triggered=~triggered;
if((c1lev) && (c1[0]<c1lev) && (currentc1>=c1lev) && (c1dir)) triggered=~triggered;
if((c1lev) && (c1[0]>c1lev) && (currentc1<=c1lev) && (~(c1dir))) triggered=~triggered;
c0[0] = currentc0;
c1[0] = currentc1;
current_ticks=0;
asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
triggertimeout++;
}
}
while (ReadActiveBufferADC10()); //wait for conversion to complete
//while(count--)
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
if(current_ticks/ticktest>loop){ //save if I'm into the next time period
currentc0=(ReadADC10(8));
currentc1=(ReadADC10(9));
if(triggered){
c0[loop] = currentc0;
c1[loop] = currentc1;
loop++;
} else{
if((c0lev) && (c0[0]<c0lev) && (currentc0>=c0lev) && (c0dir)) triggered=~triggered;
if((c0lev) && (c0[0]>c0lev) && (currentc0<=c0lev) && (~(c0dir))) triggered=~triggered;
if((c1lev) && (c1[0]<c1lev) && (currentc1>=c1lev) && (c1dir)) triggered=~triggered;
if((c1lev) && (c1[0]>c1lev) && (currentc1<=c1lev) && (~(c1dir))) triggered=~triggered;
c0[0] = currentc0;
c1[0] = currentc1;
current_ticks=0;
asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
triggertimeout++;
}
}
}
CloseADC10();
mT4IntEnable(1); //re-enable clock interrupt
if(triggertimeout<maxwait){ //only output data if not a timeout
for(loop=1;loop<count/scale;loop++){
if(*c0pos>=0)DrawLine((loop-1)*scale,k-((unsigned int)c0last[loop-1]*12375/s0+64)/128-*c0pos,loop*scale,k-((unsigned int)c0last[loop]*12375/s0+64)/128-*c0pos,1,0xFF);
if(*c1pos>=0)DrawLine((loop-1)*scale,k-((unsigned int)c1last[loop-1]*12375/s1+64)/128-*c1pos,loop*scale,k-((unsigned int)c1last[loop]*12375/s1+64)/128-*c1pos,1,0xFF);
if(*c0pos>=0)DrawLine((loop-1)*scale,k-((unsigned int)c0[loop-1]*12375/s0+64)/128-*c0pos,loop*scale,k-((unsigned int)c0[loop]*12375/s0+64)/128-*c0pos,1,0xFFFFFF);
if(*c1pos>=0)DrawLine((loop-1)*scale,k-((unsigned int)c1[loop-1]*12375/s1+64)/128-*c1pos,loop*scale,k-((unsigned int)c1[loop]*12375/s1+64)/128-*c1pos,1,0xFFFFFF);
}
} else {
scale=maxwait; //set timeout function return
}
// redraw the graticules
for(loop=0;loop<240;loop+=30)DrawLine(0,loop,319,loop,1,0x808080);
for(loop=0;loop<320;loop+=32)DrawLine(loop,0,loop,239,1,0x808080);
DrawLine(0,239,319,239,1,0x808080);
DrawLine(319,0,319,239,1,0x808080);
return scale;
}
 
piclover
Senior Member

Joined: 14/06/2015
Location: France
Posts: 134
Posted: 01:41pm 26 Oct 2015
Copy link to clipboard 
Print this post

I see some optimizations that could help speeding up the CFunction. First, by reducing the amount of if/else clauses and tests in each if, in the acquisition loop, like so:

.../...
if (triggered) {
c0[loop] = currentc0;
c1[loop++] = currentc1; /* faster than c1[loop] = currentc1; loop++; */
} else {
if (c0lev) {
if (c0dir) {
if (c0[0] < c0lev && currentc0 >= c0lev) triggered = ~triggered;
} else {
if (c0[0] > c0lev && currentc0 <= c0lev) triggered = ~triggered;
}
}
if (c1lev) {
if (c1dir) {
if (c1[0] < c1lev && currentc1 >= c1lev) triggered = ~triggered;
} else {
if (c1[0] > c1lev && currentc1 <= c1lev) triggered = ~triggered;
}
}
}
.../...


And also, in the drawing routine, by putting the tests out of the loop, using two loops instead:

count = count / scale; /* Since count is not reused later, let's avoid a division at each loop */
if (*c0pos >= 0) {
for (loop = 1; loop < count; ++loop) {
DrawLine((loop - 1) * scale, k - ((unsigned int)c0last[loop - 1] * 12375 / s0 + 64) / 128 - *c0pos, loop * scale, k - ((unsigned int) c0last[loop] * 12375 / s0 + 64) / 128 - *c0pos, 1, 0xFF);
DrawLine((loop - 1) * scale, k - ((unsigned int)c0[loop - 1] * 12375 / s0 + 64) / 128 - *c0pos, loop * scale, k - ((unsigned int)c0[loop] * 12375 / s0 + 64) / 128 - *c0pos, 1, 0xFFFFFF);
}
}
if (*c1pos >= 0) {
for (loop = 1; loop < count; ++loop) {
DrawLine((loop - 1) * scale, k - ((unsigned int)c1last[loop - 1] * 12375 / s1 + 64) / 128 - *c1pos, loop * scale, k - ((unsigned int)c1last[loop] * 12375 / s1 + 64) / 128 - *c1pos, 1, 0xFF);
DrawLine((loop - 1) * scale, k - ((unsigned int)c1[loop - 1] * 12375 / s1 + 64) / 128 - *c1pos, loop * scale, k - ((unsigned int)c1[loop] * 12375 / s1 + 64) / 128 - *c1pos, 1, 0xFFFFFF);
}
}
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2023