Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 20:29 20 Apr 2024 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 : Tet another CSUB for capturing digital data with the picomite

Author Message
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5886
Posted: 05:57am 26 Sep 2022
Copy link to clipboard 
Print this post

Peter started with the LOG CSUB for capturing digital signals.
Gerry (disco4now) expanded on that with his Manchester code CSUB.

While the original CSUB could be used, I decided to have a go at modifying it to better suit my aims.
My C skills are woeful so I pinched bits of both Peters and Gerry's code to make something that works.
I have called my effort FETCH.
One difference to Peters original is, the timing starts from when the CSUB is called, not from when the picomite started.
There are 4 modes, each giving a slightly different sequence of data.

This is a comparison of the output from a similar input signal.
mode 0     mode 1     mode 2   mode 3    
                             
174        200        207      194
-585        609       -410      409
998        1025       415      416
-1418       1445      -420      420
1835       1859       414      413
-2256       2281      -420      421
2664       2691       411      411
-3080       3105      -421      419
3498       3524       412      414
-3916       3941      -417      417
4331       4358       417      418
-4751       4777      -420      419
5166       5193       415      414
-5588       5613      -420      420
5997       6023       411      413
-6338       6364      -350      340
6550       6577       215      212
-6753       6779      -204      202
6967       6993       213      214
-7173       7198      -202      206
7384       7411       213      212
-7587       7614      -205      203
7799       7826       213      213
-8004       8029      -204      204
8216       8243       213      213


The program I used for testing uses the 'Kansas City Tape Standard' Each character takes 37mS and I find a gap between characters of 5mS gives good reliability.
Mode 1 is the one I use in the example code while I used mode 2 when setting up the comparitor I used to convert the sine wave to square wave. There I was looking for equal times for each half cycle.

Because of the way I use it, the first period is about 200 uS short. That's the time for the Basic code to initiate the CSUB once triggered.

The CSUB
'File fetch.bas written 26-09-2022 12:57:31 v1.44
' FETCH basicarray, samplecount, timeout, mode, nulltime, result
'
' basicarray is an integer array to store the resulting data. It must be at least the size of samplecount.
' samplecount is an integer with the maximum number of samples to record.
' timeout is an integer with the maximum time in microseconds that the csub will run.
' mode is an integer that defines 4 modes (0 - 3)
' mode 0 returns the microseconds since the start of the csub with positive for positive going transitions and negative for negative going transitions.
' mode 1 returns the microseconds since the start of the csub, all are positive numbers.
' mode 2 returns the microseconds since the previous transition, positive for positive going and negative for negative going.
' mode 3 returns the microseconds since the previous transition, all are positive numbers.
' nulltime is an integer giving to time in microseconds without any transition to causee an abort.
' result is an integer variable that returns (hopefully) useful information.
' result will contain a positive number representing the total time in microseconds if the csub ends due to maximum count being reached.
' result will return a negative number representing the remaining samples not read dif the csun times out.

CSUB FETCH
00000089
'check_timer
B084B5B0 4B21AF00 3320681B 4B1F60FB 3330681B 4B1D60BB 3340681B 4B1B607B
3370681B 4B1A603B 4798681B 681A68FB 4299685B D101D81A D8174290 681B4B14
687B4798 001C681B 001D2300 681A683B 18A4685B 68BB415D 685B681A 416B1912
D8034299 4290D10E E00BD800 681B4B09 601A2200 681B4B08 601A2200 681B4B07
601A2201 46BD46C0 BDB0B004 1000038C 100003AC 10000388 100003C8 100003D0
'intprog
B08EB5B0 4B59AF00 637B681B 681B4B57 633B3310 681B4B55 62FB3330 681B4B53
62BB3340 681B4B51 627B3350 681B4B4F 623B3360 681B4B4D 61FB3370 681B6B7B
6A7B61BB 617B681B 681B4B49 6AFB4798 685B681A 41991A80 000B0002 60FB60BA
681A69FB 68B8685B 1A8068F9 00024199 603A000B 69F9607B 68FB68BA 604B600A
681B6A3B D1162B00 681B4B3A 47982009 D0051E03 68BA69B9 600A68FB E00A604B
68FB68BA 21002000 41991A80 000B0002 600A69B9 6A3B604B 2B01681B 69B9D104
68FB68BA 604B600A 681B6A3B D1162B02 681B4B28 47982009 D0051E03 683A69B9
600A687B E00A604B 687B683A 21002000 41991A80 000B0002 600A69B9 6A3B604B
2B03681B 69B9D104 687B683A 604B600A 681B6B3B 697B425A 17D20014 601C0015
6B7B605D 3308681B 6B7B001A 6B3B601A 1E5A681B 601A6B3B 681B6B3B DC102B00
68BA6979 600A68FB 4B0B604B 2200681B 4B0A601A 2200681B 4B09601A 2201681B
46C0601A B00E46BD 46C0BDB0 1000038C 100003AC 1000032C 10000388 100003C8
100003D0
'main
B08CB580 60F8AF00 607A60B9 4B2E603B 4A2E681B 601A447A 681B4B2D 447A4A2D
4B2D601A 62FB681B 681B4B2B 62BB3310 681B4B29 627B3320 681B4B27 623B3330
681B4B25 61FB3340 681B4B23 61BB3350 681B4B21 617B3360 681B4B1F 613B3370
6AFB68FA 68BB601A 685B681A 601A6ABB 681B4B1A 00024798 6879000B 68496808
414B1812 600A6A79 4B14604B 4798681B 000B0002 600A6A39 6939604B 23002200
604B600A 681A683B 697B685B 6BBB601A 685B681A 601A69FB 69BB6BFA 46C0601A
B00C46BD 46C0BD80 100003C8 FFFFFE65 10000388 FFFFFDBB 1000038C 100003AC
END CSUB


The source code
#include "PicoCFunctions.h"
// uSecTimer returns a 64-bit integer giving the number of microseconds since system boot
// CFuncRam is 256 bytes of RAM that aren't touched by Basic
// PinRead gives the status of a pin specified by the number (pin9 = GP6)
// CFuncInt1 is the address of a function to be called when a H/W interrupt occurs on COUNT pin 1
// CFuncmSec is the address of a function to be called every millisecond by trhe main clock interrupt
// Interrupt is a variable that tells Basic that the INTERRUPT specified in the Basic code has been triggered
//
// main(basicarray, samplecount, timeout, mode, nulltime, result)
//
// basicarray is an integer array to store the resulting data. It must be at least the size of samplecount.
// samplecount is an integer with the maximum number of samples to record.
// timeout is an integer with the maximum time in microseconds that the csub will run.
// mode is an integer that defines 4 modes (0 - 3)
// mode 0 returns the microseconds since the start of the csub with positive for positive going transitions and negative for negative going transitions.
// mode 1 returns the microseconds since the start of the csub, all are positive numbers.
// mode 2 returns the microseconds since the previous transition, positive for positive going and negative for negative going.
// mode 3 returns the microseconds since the previous transition, all are positive numbers.
// nulltime is an integer giving to time in microseconds without any transition to causee an abort.
// result is an integer variable that returns (hopefully) useful information.
// result will contain a positive number representing the total time in microseconds if the csub ends due to maximum count being reached.
// result will return a negative number representing the remaining samples not read dif the csun times out.

static void check_timer(void){ //routine called every millisecond
unsigned long long int *endtime=(unsigned long long int *)&CFuncRam[8];
unsigned long long int *starttime=(unsigned long long int *)&CFuncRam[12];
unsigned int *ntime=(unsigned int *)&CFuncRam[16];
unsigned long long int *lasttime=( unsigned long long int *)&CFuncRam[28];
if(uSecTimer()>*endtime || uSecTimer()>(*ntime+ *lasttime+ *starttime)){ //  timeout triggered
CFuncmSec=0; //disable the millisecond interrupt
CFuncInt1=0; //disable the H/W pin change interrupt
Interrupt=1; //trigger the Basic interrupt
}
}
static void intprog(void){ //routine called every change on GP6
unsigned int *array=(unsigned int *)&CFuncRam[0];
int *count=(int *)&CFuncRam[4];
unsigned long long int *starttime=(unsigned long long int *)&CFuncRam[12];
unsigned int *res=(unsigned int *)&CFuncRam[20];
int *mode=(int *)&CFuncRam[24];
unsigned long long int *lasttime=( unsigned long long int *)&CFuncRam[28];
 unsigned long long int nowusec;
 unsigned long long int thisusec;
 
unsigned int d=*array;
unsigned int r=*res;

nowusec=uSecTimer()- *starttime;
 thisusec=nowusec- *lasttime;
 *lasttime=nowusec;
 
if (*mode==0){
if(PinRead(9))*(long long int *)d=nowusec;
else *(long long int *)d= -nowusec;
}

if (*mode==1){
*(long long int *)d=nowusec;
}

if (*mode==2){
if(PinRead(9))*(long long int *)d=thisusec;
else *(long long int *)d= -thisusec;
}
if (*mode==3){
*(long long int *)d=thisusec;
}
*(long long int *)r= -*count;
*array=*array+sizeof(long long int);
*count=*count-1; //decrement the event counter
if(*count<=0){ //count satisfied
*(long long int *)r=nowusec;
CFuncmSec=0; //disable the millisecond interrupt
CFuncInt1=0; //disable the H/W pin change interrupt
Interrupt=1; //trigger the Basic interrupt
}
//if(PinRead(9))*(long long int *)d=uSecTimer();
//else *(long long int *)d=-uSecTimer();

}
void main(long long int *basicarray, long long int *samplecount, long long int *timout, long long int *mde,long long int *nulltime,long long int *result){
CFuncInt1=(unsigned int)&intprog; //set up the address for the H/W interrupt
CFuncmSec=(unsigned int)&check_timer; //Set up the address for the millisecond timer
unsigned int *array=(unsigned int *)&CFuncRam[0]; //get a pointer to a permanent location to store a global pointer to the array
int *count=(int *)&CFuncRam[4]; //get a pointer to a permanent location to store the count of the samples required
unsigned long long int *endtime=(unsigned long long int *)&CFuncRam[8]; //get a pointer to a permanent location to store the timeout value
unsigned long long int *starttime=(unsigned long long int *)&CFuncRam[12];
unsigned int *ntime=(unsigned int *)&CFuncRam[16];
unsigned int *res=(unsigned int *)&CFuncRam[20];
int *mode=(int *)&CFuncRam[24];
unsigned long long int *lasttime=( unsigned long long int *)&CFuncRam[28];
*array=(unsigned int)basicarray;
*count=(int)*samplecount;
*endtime=uSecTimer()+ *timout;
*starttime=uSecTimer();
*lasttime=0;
*mode=(int)*mde;
*ntime=(unsigned int)*nulltime;
*res=(unsigned int)result;
}


The KCTS sending program
' KCTS TassyJim Sep 2022
OPTION EXPLICIT
OPTION DEFAULT INTEGER


 CONST char_time = 4.0    ' time to wait between characters
 
DIM bit_time! = 3.2       ' should be 3.33 less processing time.
DIM bits(10), message$, k$

 message$ = "Hello world"
   sendtxt message$
     PAUSE 2000
   DO
     k$ = INKEY$
     IF k$ <> "" THEN
       sendtxt k$
     ENDIF
   LOOP
' these two subs are all thats needed to send text
SUB sendtxt txt$
 LOCAL n
 IF LEN(txt$) > 0 THEN
   FOR n = 1 TO LEN(txt$)
     sendchar MID$(txt$,n,1)
     PAUSE char_time
   NEXT n
 ENDIF
END SUB
 
SUB sendchar ch$
 LOCAL n, t!, et!
t! = TIMER
 bits(0) = 0                      ' start bit
 FOR n = 1 TO 8
   bits(n) = (ASC(ch$) >>(n-1)) AND 1 ' bits sent lsb first
 NEXT n
 bits(9) = 1                      ' 2 stop bits
 bits(10) = 1
 FOR n = 0 TO 10
   PLAY TONE 1200*(bits(n)+1),1200*(bits(n)+1) ' 0 = 1200, 1 = 2400
   PAUSE bit_time!
 NEXT n
 PLAY STOP
et! = TIMER
' print et!-t!, bit_time!
bit_time! = bit_time! + (36.6667-(et!-t!))/11 'adjust bit_time so that the time for all 11 bits = 36.6667
END SUB


and the matching receive program.
' KCTS TassyJim Sep 2022
 OPTION EXPLICIT
 OPTION DEFAULT INTEGER
 DIM bits(10)
' DIM starttime!, midtime!, bt!
 DIM n, p, done, k$, char$
 DIM bit_time! = 3.2       ' should be 3.33 less processing time.
 CONST bit_bounds = 1200    ' this is the time for two cycles half way between 1200 and 2400 Hz
 
 INTERRUPT myint 'set up an interrupt that can be triggered by the CSUB
'
 p = 1
 SETPIN gp15, INTB, startrec
 SETPIN gp14, DOUT ' used for timing on the CRO
 SETPIN gp6,CIN,3 'set pin 9 (GP6) to cause a H/W interrupt on both edges
'
 DIM aa%(200) 'array to receive the timestamps
 DIM ab%(200)
 DIM ac%(200)
 DIM b%=172 'maximum number of transitions to receive
 DIM c%=36900 'timeout of the CSUB in microseconds
 DIM d% = 1 ' csub mode
 DIM e% = 1000 ' no signal timeout
 DIM f% ' csub result
'
 DO
   IF done = 1 THEN ' we have finished receiving a character
     done = 0
     mark 1 ' used for timing measurements
     char$ = recchar$()
     PRINT char$;
     mark 0
   ENDIF
 LOOP
 
 
SUB startrec
 SETPIN gp15, OFF
'
 IF p THEN
   FETCH aa%(),b%,c%,d%,e%,f%
 ELSE
   FETCH ab%(),b%,c%,d%,e%,f%
 ENDIF
END SUB
 
SUB myint 'subroutine that is triggered when the non-blocking CSUB terminates
 LOCAL INTEGER i
 LOCAL b, n, char_asc, bt!,offset!, level!
' print f%
 mark 1 ' pulse for timing with CRO
 p = 1 - p
 SETPIN gp15, INTB, startrec
 
 IF p THEN
   MATH add ab%(), 0, ac%()
   MATH set 0, ab%()
 ELSE
   MATH add aa%(), 0, ac%()
   MATH set 0, aa%()
 ENDIF
 mark 0
 done = 1
 
END SUB
 
' routines used for receiving and decoding KCTS
 
' parse the array of received timing and extract the 8 bit byte
FUNCTION recchar$()
 LOCAL b, n, char_asc, bt!,offset!, level!
'for n = 0 to 24
'print ac%(n)
'next n

 bt! = MATH(MAX ac%())/11 ' the 11 bits should take 36.6667 mS but we adjust as required
 offset! = bt!/1.8 ' take the first sample shortly after half way throug it.
 FOR n = 4 TO 200
   IF ac%(n) = 0 THEN : EXIT FOR : ENDIF
   IF b > 10 THEN : EXIT FOR : ENDIF
   IF ac%(n) > offset! THEN
     level! =  ac%(n)-ac%(n-4)
     IF level! < bit_bounds THEN ' we average over 4 half cycles
       bits(b) = 1
     ELSE
       bits(b) = 0
     ENDIF
     offset! = offset! + bt! ' advance to the next bit timeslot
     INC b
   ENDIF
 NEXT n
 FOR n = 8 TO 1 STEP -1
   char_asc = (char_asc << 1) + bits(n) ' lsb received first
 NEXT n
 recchar$ = CHR$(char_asc)
END FUNCTION
 
SUB mark x
 PIN(gp14) = x
END SUB
 
CSUB FETCH
00000089
'check_timer
B084B5B0 4B21AF00 3320681B 4B1F60FB 3330681B 4B1D60BB 3340681B 4B1B607B
3370681B 4B1A603B 4798681B 681A68FB 4299685B D101D81A D8174290 681B4B14
687B4798 001C681B 001D2300 681A683B 18A4685B 68BB415D 685B681A 416B1912
D8034299 4290D10E E00BD800 681B4B09 601A2200 681B4B08 601A2200 681B4B07
601A2201 46BD46C0 BDB0B004 1000038C 100003AC 10000388 100003C8 100003D0
'intprog
B08EB5B0 4B59AF00 637B681B 681B4B57 633B3310 681B4B55 62FB3330 681B4B53
62BB3340 681B4B51 627B3350 681B4B4F 623B3360 681B4B4D 61FB3370 681B6B7B
6A7B61BB 617B681B 681B4B49 6AFB4798 685B681A 41991A80 000B0002 60FB60BA
681A69FB 68B8685B 1A8068F9 00024199 603A000B 69F9607B 68FB68BA 604B600A
681B6A3B D1162B00 681B4B3A 47982009 D0051E03 68BA69B9 600A68FB E00A604B
68FB68BA 21002000 41991A80 000B0002 600A69B9 6A3B604B 2B01681B 69B9D104
68FB68BA 604B600A 681B6A3B D1162B02 681B4B28 47982009 D0051E03 683A69B9
600A687B E00A604B 687B683A 21002000 41991A80 000B0002 600A69B9 6A3B604B
2B03681B 69B9D104 687B683A 604B600A 681B6B3B 697B425A 17D20014 601C0015
6B7B605D 3308681B 6B7B001A 6B3B601A 1E5A681B 601A6B3B 681B6B3B DC102B00
68BA6979 600A68FB 4B0B604B 2200681B 4B0A601A 2200681B 4B09601A 2201681B
46C0601A B00E46BD 46C0BDB0 1000038C 100003AC 1000032C 10000388 100003C8
100003D0
'main
B08CB580 60F8AF00 607A60B9 4B2E603B 4A2E681B 601A447A 681B4B2D 447A4A2D
4B2D601A 62FB681B 681B4B2B 62BB3310 681B4B29 627B3320 681B4B27 623B3330
681B4B25 61FB3340 681B4B23 61BB3350 681B4B21 617B3360 681B4B1F 613B3370
6AFB68FA 68BB601A 685B681A 601A6ABB 681B4B1A 00024798 6879000B 68496808
414B1812 600A6A79 4B14604B 4798681B 000B0002 600A6A39 6939604B 23002200
604B600A 681A683B 697B685B 6BBB601A 685B681A 601A69FB 69BB6BFA 46C0601A
B00C46BD 46C0BD80 100003C8 FFFFFE65 10000388 FFFFFDBB 1000038C 100003AC
END CSUB

I fed the audio output form the sending pico to a comparitor then into the receiving pico.
I use two inputs, one to GP6 for the CSUB and linked it to GP15 through a resistor for the trigger.

Jim
VK7JH
MMedit   MMBasic Help
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 06:17am 26 Sep 2022
Copy link to clipboard 
Print this post

Is there a tutorial for creating PM CSubs?



Craig
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5886
Posted: 07:01am 26 Sep 2022
Copy link to clipboard 
Print this post

This post is where I started:
https://www.thebackshed.com/forum/ViewTopic.php?TID=14126&P=1

Jim
VK7JH
MMedit   MMBasic Help
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1773
Posted: 07:56am 26 Sep 2022
Copy link to clipboard 
Print this post

Thanks for posting, this looks useful for all sorts of things.
 
disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 843
Posted: 10:12am 26 Sep 2022
Copy link to clipboard 
Print this post

Thanks Jim,
I have added to my collection of code that I can cut and paste from.
Gerry
Latest F4 Latest H7
 
Print this page


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

© JAQ Software 2024