Add files via upload
This commit is contained in:
parent
1986908a5a
commit
1dd49b43aa
8 changed files with 2212 additions and 0 deletions
23
pico_pgen/CMakeLists.txt
Normal file
23
pico_pgen/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(test_project C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(pico_sdk_pgen
|
||||
pico_sdk_pgen.c
|
||||
)
|
||||
|
||||
pico_enable_stdio_usb(pico_sdk_pgen 0)
|
||||
pico_enable_stdio_uart(pico_sdk_pgen 1)
|
||||
|
||||
pico_add_extra_outputs(pico_sdk_pgen)
|
||||
|
||||
target_link_libraries(
|
||||
pico_sdk_pgen
|
||||
pico_stdlib
|
||||
|
||||
)
|
||||
51
pico_pgen/pico_sdk_pgen.c
Normal file
51
pico_pgen/pico_sdk_pgen.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//simple generator
|
||||
//Mask of bits 22:2 to use as outputs -
|
||||
#define GPIO_D_MASK 0x7FFFFC
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "stdarg.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int main(){
|
||||
|
||||
long int i,j;
|
||||
|
||||
// set_sys_clock_khz(250000,true);
|
||||
// uart_set_format(uart0,8,1,1);
|
||||
// uart_init(uart0,115200);
|
||||
stdio_init_all();
|
||||
gpio_set_function(0, GPIO_FUNC_UART);
|
||||
gpio_set_function(1, GPIO_FUNC_UART);
|
||||
sleep_us(100000);
|
||||
printf("\n\n\nhello from pgen \r\n\n\n");
|
||||
//Use 22:2 as digital
|
||||
gpio_init_mask(GPIO_D_MASK); //set as GPIO_FUNC_SIO and clear output enable
|
||||
gpio_set_dir_masked(GPIO_D_MASK,GPIO_D_MASK); //TODO - I think an out can still be an ind?
|
||||
uint32_t ctime,tstart;
|
||||
uint32_t cval;
|
||||
tstart=time_us_32();
|
||||
while(true){
|
||||
ctime=time_us_32();
|
||||
// shift up/down to test performance with rle etc
|
||||
//cval=(ctime-tstart)<<4; //not a true 62.5ns/8Mhz wave, but will increment as such
|
||||
//cval=(ctime-tstart)<<2; //not a true 250ns/2Mhz wave, but will increment as such
|
||||
//cval=(ctime-tstart); //1us, 500kz
|
||||
//cval=(ctime-tstart)>>1; //2us, 256khz
|
||||
//cval=(ctime-tstart)>>3; //8us, 64khz
|
||||
cval=(ctime-tstart)>>5; //32us, 16khz
|
||||
//cval=(ctime-tstart)>>6; //64us, 8khz
|
||||
//cval=(ctime-tstart)>>7; //128us, 4khz
|
||||
//cval=(ctime-tstart)>>9; //512us, 1khz
|
||||
//cval=(ctime-tstart)>>11; //2ms, 250hz
|
||||
//cval=0x123456789;
|
||||
//Note: both the mask and the value must be shifted up by the starting number of bits
|
||||
gpio_put_masked(GPIO_D_MASK,cval<<2);
|
||||
//printf("cval 0x%X",cval);
|
||||
}//while true
|
||||
|
||||
|
||||
|
||||
}//main
|
||||
26
pico_sdk_sr_gen1/CMakeLists.txt
Normal file
26
pico_sdk_sr_gen1/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(test_project C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(pico_sdk_sr_gen_1
|
||||
pico_sdk_sr_gen_1.c
|
||||
)
|
||||
|
||||
pico_enable_stdio_usb(pico_sdk_sr_gen_1 1)
|
||||
pico_enable_stdio_uart(pico_sdk_sr_gen_1 0)
|
||||
|
||||
pico_add_extra_outputs(pico_sdk_sr_gen_1)
|
||||
|
||||
target_link_libraries(
|
||||
pico_sdk_sr_gen_1
|
||||
pico_stdlib
|
||||
hardware_adc
|
||||
hardware_dma
|
||||
hardware_pio
|
||||
pico_multicore
|
||||
)
|
||||
171
pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c
Normal file
171
pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//1st version to communicate over sigrok generic
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "stdarg.h"
|
||||
#include <string.h>
|
||||
const uint LED_PIN = 25;
|
||||
#define MAX_FMT_SIZE 256
|
||||
#define MAX_SAMPLE_RATE 125000000
|
||||
#define MAX_SAMPLES 100000
|
||||
|
||||
int Dprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
int len = 1;
|
||||
char _dstr[256];
|
||||
|
||||
|
||||
memset(&_dstr, 0x00, sizeof(_dstr) );
|
||||
va_start(argptr, fmt);
|
||||
len = vsprintf(_dstr, fmt, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
if ( (len > 0) && (len < 256 ) )
|
||||
{
|
||||
|
||||
uart_puts(uart0,_dstr);
|
||||
uart_puts(uart0,"\r\n");
|
||||
}
|
||||
return len;
|
||||
}
|
||||
int main(){
|
||||
char cmdstr[20];
|
||||
int cmdstrptr=0;
|
||||
char charin,tmpchar;
|
||||
int intin;
|
||||
long int intin2,i,j,idx;
|
||||
int cnt=0;
|
||||
int armed=0;
|
||||
int num_a=3,num_d=2;
|
||||
long int sample_rate=125000000; //max sample rate is initial value
|
||||
unsigned long int scnt,num_samples=5;
|
||||
char tmpstr[100],a_bytes=3;
|
||||
long int tmpint,tmpint2;
|
||||
int res;
|
||||
uint64_t starttime,endtime;
|
||||
// sleep_ms(1000); //might help stability???
|
||||
stdio_usb_init();
|
||||
// sleep_ms(1000);
|
||||
uart_set_format(uart0,8,1,1);
|
||||
intin=uart_init(uart0,115200);
|
||||
|
||||
gpio_set_function(0, GPIO_FUNC_UART);
|
||||
gpio_set_function(1, GPIO_FUNC_UART);
|
||||
//doesn't seem to help stabiliyt sleep_ms(3000);
|
||||
Dprintf("hello from loggerport \r\n");
|
||||
intin=0;
|
||||
while(1){
|
||||
intin=getchar_timeout_us(0);//000);
|
||||
if(intin>=0){ //PICO_ERROR_TIMEOUT=-1
|
||||
charin=(char)intin;
|
||||
//TODO - SCPI may only send the \n, not \r...
|
||||
if((charin=='\r')||(charin=='\n')){
|
||||
cmdstr[cmdstrptr]=0;
|
||||
Dprintf("Cmd %s",cmdstr);
|
||||
if(!strcmp(cmdstr,"*IDN?")){
|
||||
//AxxxyDzzz - num analog, analog size, num digital
|
||||
//24 characters plus newline
|
||||
// printf("SR_GENERIC,A0023D002,0,0\n");
|
||||
printf("SR_GENERIC,A%03d%dD%03d,0,0\n",num_a,a_bytes,num_d);
|
||||
Dprintf("sent IDN response");
|
||||
}
|
||||
else if(!strncmp(cmdstr,"SRT",3)){
|
||||
if(cmdstr[3]=='?'){
|
||||
printf("%d\n",sample_rate);
|
||||
Dprintf("smp rate rsp %d",sample_rate);
|
||||
}else{
|
||||
tmpint=atoi(&cmdstr[3]);
|
||||
if((tmpint>0)&&(tmpint<=MAX_SAMPLE_RATE)){
|
||||
sample_rate=tmpint;
|
||||
Dprintf("set smp rate to %d",sample_rate);
|
||||
}else{
|
||||
Dprintf("unsupported smp rate %s",cmdstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!strncmp(cmdstr,"SMP",3)){
|
||||
if(cmdstr[3]=='?'){
|
||||
printf("%d\n",num_samples);
|
||||
Dprintf("num smp rate rsp %d",num_samples);
|
||||
}else{
|
||||
tmpint=atoi(&cmdstr[3]);
|
||||
if((tmpint>0)&&(tmpint<=MAX_SAMPLES)){
|
||||
num_samples=tmpint;
|
||||
Dprintf("set num smp to %d",num_samples);
|
||||
}else{
|
||||
Dprintf("unsupported num samples %s",cmdstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!strncmp(cmdstr,"ARM",3)){
|
||||
armed=1;
|
||||
scnt=0;
|
||||
Dprintf("armed");
|
||||
starttime=time_us_64();//get_absolute_time();
|
||||
}
|
||||
else if(!strncmp(cmdstr,"STP",3)){
|
||||
//TODO -send end of capture if armed...
|
||||
armed=0;
|
||||
scnt=0;
|
||||
Dprintf("stopped");
|
||||
}
|
||||
cmdstrptr=0;
|
||||
|
||||
}else{
|
||||
// sprintf(tmpstr,"newchar %c %d \r\n",charin,intin);
|
||||
// uart_puts(uart0,tmpstr);
|
||||
if(cmdstrptr>=19){
|
||||
cmdstrptr=0;
|
||||
Dprintf("Command overflow");
|
||||
}
|
||||
cmdstr[cmdstrptr++]=charin;
|
||||
|
||||
}
|
||||
}//intin>=0
|
||||
//I modified the pico sdk to add a puts_raw_len function so that I could send byte patterns of fixed length
|
||||
//that were not c strings. But, going to use ascii compliant values to avoid serial configuration issues
|
||||
//and thus can end a set of bytes with a null to make it a string
|
||||
// puts_raw_len("0000000000111111111122222222223333333333",40);
|
||||
// scnt+=4;
|
||||
|
||||
if(armed&&(scnt<num_samples)){
|
||||
idx=0;
|
||||
for(i=0;i<num_a;i++){
|
||||
//calculate an N byte value to match up with analog size
|
||||
tmpint=((scnt+i)*10000)&((1<<(8*a_bytes))-1);
|
||||
// Dprintf("A%d val %d",i,tmpint);
|
||||
for(j=0;j<a_bytes;j++){
|
||||
//get one byte - least sig byte first
|
||||
tmpint2=(tmpint>>(j*8))&0xFF;
|
||||
// Dprintf("tmpint2 %d",tmpint2);
|
||||
//convert to ascii mode - lowest nibble first
|
||||
tmpstr[idx++]=((char)(tmpint2&0xF))+'0';
|
||||
tmpstr[idx++]=((char)((tmpint2>>4)&0xF))+'0';
|
||||
// Dprintf("chars %c %c",tmpstr[idx-2],tmpstr[idx-1]);
|
||||
}
|
||||
} //for num_a
|
||||
for(i=0;i<num_d;i+=4){
|
||||
//todo this could use better masking based on num channels, but will help find bugs if
|
||||
//we use bits we shouldnt. Since we send one char per 4 bits
|
||||
tmpint=(scnt>>i)&0xf;
|
||||
// Dprintf("D%d val %d",i,tmpint);
|
||||
tmpstr[idx++]=(tmpint&0xF)+'0';
|
||||
// Dprintf("chars %c ",tmpstr[idx-1]);
|
||||
}
|
||||
tmpstr[idx]=0;
|
||||
// puts_raw(tmpstr);
|
||||
printf("%s",tmpstr);
|
||||
Dprintf("Slice string %s",tmpstr);
|
||||
scnt++;
|
||||
if(scnt==num_samples){
|
||||
armed=0;
|
||||
endtime=time_us_64();
|
||||
Dprintf("Total time %llu %llu %llu",(endtime-starttime),endtime,starttime);
|
||||
}
|
||||
} //if armed
|
||||
}//while(1)
|
||||
|
||||
|
||||
}//main
|
||||
371
pico_sdk_sr_gen1/sr_device.h
Normal file
371
pico_sdk_sr_gen1/sr_device.h
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
//number of analog channels
|
||||
//#define NUM_A_CHAN 3
|
||||
//TODO - testing a D4 only build to see if it's faster because I don't
|
||||
//need to replicate unused channel data
|
||||
#define NUM_A_CHAN 0
|
||||
//number of digital channels
|
||||
//GP0 and 1 are reserved for uart
|
||||
//GP23-25 are not on the board and 26-28 are ADC.
|
||||
//Thus a max of 21 digital channels
|
||||
//#define NUM_D_CHAN 21
|
||||
|
||||
#define NUM_D_CHAN 4
|
||||
//Mask of bits 22:2 to use as inputs -
|
||||
#define GPIO_D_MASK 0x7FFFFC
|
||||
//number of bytes per analog sample, must be 1,2,3,or 4
|
||||
//TODO - make it just be always 1
|
||||
#define A_BYTES 1
|
||||
//enable RLE encoding
|
||||
#define RLE_EN 1
|
||||
//TODO -finalize a value
|
||||
#define RLE_THRESH 2000
|
||||
//number of chars between makers
|
||||
#define STRIDE 128
|
||||
//TODO - implement various limits on sample rate
|
||||
#define MAX_SAMPLE_RATE 120000000
|
||||
//warning - with a 256b limit in the Dprintf, large strings won't dprintf well
|
||||
#define MRK_STR_SIZE 240 //TODO - larger for testing puts_raw_len
|
||||
//TODO - long term prints don't belong in this code
|
||||
int Dprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
int len = 1;
|
||||
char _dstr[256];
|
||||
|
||||
|
||||
memset(&_dstr, 0x00, sizeof(_dstr) );
|
||||
va_start(argptr, fmt);
|
||||
len = vsprintf(_dstr, fmt, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
if ( (len > 0) && (len < 240 ) )
|
||||
{
|
||||
|
||||
uart_puts(uart0,_dstr);
|
||||
uart_tx_wait_blocking(uart0);
|
||||
//don't always want \n
|
||||
// uart_puts(uart0,"\r\n");
|
||||
// uart_tx_wait_blocking(uart0);
|
||||
}
|
||||
else{
|
||||
uart_puts(uart0,"UART OVRFLW");
|
||||
uart_tx_wait_blocking(uart0);
|
||||
|
||||
}
|
||||
return len;
|
||||
}
|
||||
typedef struct sr_device {
|
||||
uint32_t sample_rate;
|
||||
uint32_t num_samples;
|
||||
uint32_t a_mask,d_mask;
|
||||
uint32_t byte_cnt; //total serial bytes sent
|
||||
uint32_t samples_per_half; //number of samples for one of the 4 dma target arrays
|
||||
uint8_t a_chan_cnt; //count of enabled analog channels
|
||||
uint8_t d_chan_cnt; //count of enabled digital channels
|
||||
//Pins sampled by the PIO - 4,8,16 or 32
|
||||
uint8_t pin_count;
|
||||
uint8_t d_nps; //digital nibbles per slice from a PIO/DMA perspective.
|
||||
uint32_t scnt; //number of samples sent
|
||||
char cmdstrptr;
|
||||
char cmdstr[20];//used for parsing input
|
||||
uint32_t d_size,a_size; //size of each of the two data buffers for each of a& d
|
||||
uint32_t dbuf0_start,dbuf1_start,abuf0_start,abuf1_start;
|
||||
//TODO optimize this - this is the size of string we send , for now deriving from sample size
|
||||
uint32_t send_size;
|
||||
char rspstr[20];
|
||||
//The current slice being created
|
||||
char slicestr[64];
|
||||
char slicestridx;
|
||||
#ifdef RLE_EN
|
||||
char prevslicestr[64];
|
||||
uint32_t rle_cnt;
|
||||
#endif
|
||||
//String with markers inserted used to send the final transmit data
|
||||
uint32_t mrkbyte_cnt;
|
||||
char mrkstr[MRK_STR_SIZE];
|
||||
uint16_t mrkstridx;
|
||||
//mark key control variables voltatile since multiple cores access them
|
||||
volatile bool started;
|
||||
volatile bool sending;
|
||||
volatile bool cont;
|
||||
volatile bool aborted;
|
||||
} sr_device_t;
|
||||
|
||||
//Add one 4 bit equivalent character
|
||||
//TODO - I don't think the inlines help.
|
||||
void inline add_slice_char(sr_device_t *d,char bytein){
|
||||
d->slicestr[d->slicestridx++]=bytein;
|
||||
};
|
||||
|
||||
|
||||
//returns 0 if no string to send
|
||||
//otherwise returns the length of the string that is in d->mrkstridx which needs to be printed
|
||||
uint32_t slice_done(sr_device_t *d){
|
||||
bool almost_done;
|
||||
// int almost_done;
|
||||
almost_done =((d->cont==true)||(((d->num_samples)-(d->scnt))>2)) ? false: true;
|
||||
// almost_done =((((d->num_samples)-(d->scnt))>2)) ? false: true;
|
||||
// almost_done =((((d->num_samples)-(d->scnt))>2)) ? 0: 1;
|
||||
if(d->scnt>99900){
|
||||
// Dprintf("sd cnt %u ad %d cont %d ns %d\n\r",d->scnt,almost_done,d->cont,d->num_samples);
|
||||
}
|
||||
//Dprintf("sd %d\n\r",d->slicestridx);
|
||||
//terminate the current slice string
|
||||
d->slicestr[d->slicestridx]=0;
|
||||
bool match;
|
||||
#ifdef RLE_EN
|
||||
match=((d->scnt>0)&&(RLE_EN)&&(strncmp(d->prevslicestr,d->slicestr,d->slicestridx)==0)) ? true : false;
|
||||
strncpy(d->prevslicestr,d->slicestr,d->slicestridx);
|
||||
//Only increment rle if we matched, as if we don't match we just flush the previous ones.
|
||||
if(match) d->rle_cnt++;
|
||||
//If not a match we must drain all RLEs before sending the new sample.
|
||||
//The third condition ensures we don't have any rle counts that haven't been pushed to the marker
|
||||
//string, so send all slices as we get close to the end.
|
||||
if(((match==false)&&(d->rle_cnt>0))
|
||||
||(d->rle_cnt>=RLE_THRESH)
|
||||
||((d->rle_cnt>0)&&(almost_done==true))){
|
||||
uint8_t rlenum;
|
||||
char rlechar;
|
||||
// Dprintf("push rle %d match %d slicestridx %d ad %d scnt %u\n\r",d->rle_cnt,match,d->slicestridx,almost_done,d->scnt);
|
||||
while(d->rle_cnt>256){
|
||||
rlenum=(d->rle_cnt>=4096) ? 16 : (d->rle_cnt>>8);
|
||||
//subtract the 1 because 256 is starting value, not 0
|
||||
rlechar=rlenum+96-1;
|
||||
d->rle_cnt-=rlenum<<8;
|
||||
insert_mark(d,rlechar);
|
||||
//Dprintf("rle256 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt);
|
||||
}
|
||||
while(d->rle_cnt>16){
|
||||
rlenum=(d->rle_cnt>=256) ? 16: (d->rle_cnt>>4);
|
||||
rlechar=rlenum+80-1;
|
||||
d->rle_cnt-=rlenum<<4;
|
||||
insert_mark(d,rlechar);
|
||||
//Dprintf("rle16 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt);
|
||||
}
|
||||
while(d->rle_cnt>0){
|
||||
rlenum=(d->rle_cnt>=16) ? 16: (d->rle_cnt);
|
||||
rlechar=rlenum+64-1;
|
||||
d->rle_cnt-=rlenum;
|
||||
insert_mark(d,rlechar);
|
||||
//Dprintf("rle1 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt);
|
||||
}
|
||||
}//(match==false) || rle_cnt
|
||||
#else
|
||||
match=false;
|
||||
#endif
|
||||
if(match==false){
|
||||
// Dprintf("nomatch pushes slice idx %d",slicestridx);
|
||||
for(int cnt=0;cnt<d->slicestridx;cnt++){
|
||||
insert_mark(d,d->slicestr[cnt]);
|
||||
}
|
||||
|
||||
}
|
||||
//Empiricaly found that 16 bytes provides the highest bandwidth
|
||||
//likely that's because we balance new sample generation with the ability to drain a packet.
|
||||
//TODO - validate that is still the case...
|
||||
uint32_t ret;
|
||||
if(almost_done==true){
|
||||
// Dprintf("ad %u\n\r",d->scnt);
|
||||
}
|
||||
// Dprintf("curr #%s# \n\r",d->mrkstr);
|
||||
// Dprintf("curr idx %d\n\r",d->mrkstridx);
|
||||
|
||||
//TODO - 16 was before I added the sleep.. trying larger payloads with smaller sleep
|
||||
if((d->mrkstridx>d->send_size) //SPOT
|
||||
||(d->mrkstridx>=(MRK_STR_SIZE-2))
|
||||
||(almost_done==true)){
|
||||
d->mrkstr[d->mrkstridx]=0;
|
||||
// Dprintf("tx %s %d\n\r",d->mrkstr,d->mrkstridx);
|
||||
ret=d->mrkstridx;
|
||||
//TODO - hack - for the len function rely on user to clear
|
||||
// d->mrkstridx=0;
|
||||
}else{
|
||||
ret=0;
|
||||
}
|
||||
d->scnt++;
|
||||
d->slicestridx=0;
|
||||
return ret;
|
||||
|
||||
};
|
||||
//reset as part of init, or on a completed send
|
||||
void reset(sr_device_t *d){
|
||||
d->sending=0;
|
||||
d->cont=0;
|
||||
d->aborted=false;
|
||||
d->started=false;
|
||||
d->mrkbyte_cnt=0;
|
||||
d->mrkstridx=0;
|
||||
d->slicestridx=0;
|
||||
d->scnt=0;
|
||||
#ifdef RLE_EN
|
||||
d->rle_cnt=0;
|
||||
#endif
|
||||
};
|
||||
//initial post reset state
|
||||
void init(sr_device_t *d){
|
||||
reset(d);
|
||||
d->a_mask=0;
|
||||
d->d_mask=0;
|
||||
d->sample_rate=1000;
|
||||
d->num_samples=10;
|
||||
d->a_chan_cnt=0;
|
||||
d->d_nps=0;
|
||||
d->cmdstrptr=0;
|
||||
}
|
||||
void tx_init(sr_device_t *d){
|
||||
reset(d);
|
||||
d->a_chan_cnt=0;
|
||||
for(int i=0;i<NUM_A_CHAN;i++){
|
||||
if(((d->a_mask)>>i)&1){d->a_chan_cnt++;}
|
||||
}
|
||||
//Nibbles per slice controls how PIO digital data is stored
|
||||
//Only support 0,1,2,4 or 8, which use 0,4,8,16 or 32 bits of PIO fifo data
|
||||
//per sample clock.
|
||||
d->d_nps=(d->d_mask& 0xF) ? 1 : 0;
|
||||
d->d_nps=(d->d_mask& 0xF0) ? (d->d_nps)+1 : d->d_nps;
|
||||
d->d_nps=(d->d_mask& 0xFF00) ? (d->d_nps)+2 : d->d_nps;
|
||||
d->d_nps=(d->d_mask&0xFFFF0000) ? (d->d_nps)+4 : d->d_nps;
|
||||
|
||||
if((d->d_nps==0) &&(d-> a_chan_cnt==0)){
|
||||
Dprintf("Error no channels enabled\n\r");
|
||||
printf("!!!!!"); //stop transfer to host
|
||||
return;
|
||||
}
|
||||
//Digital channels must enable from D0 and go up
|
||||
int invld=0; //have we seen a digital channel that is not valid
|
||||
d->d_chan_cnt=0;
|
||||
for(int i=0;i<NUM_D_CHAN;i++){
|
||||
if(((d->d_mask)>>i)&1){
|
||||
Dprintf("i %d inv %d mask %X\n\r",i,invld,d->d_mask);
|
||||
if(invld){
|
||||
Dprintf("Error:Dig chan D%d (pin D%d) not continuous from 0 (pin D2)\n\r",i,i+2);
|
||||
printf("!!!!!"); //stop transfer to host
|
||||
return;
|
||||
|
||||
}else{
|
||||
d->d_chan_cnt++;
|
||||
}
|
||||
}else{
|
||||
invld=1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
d->sending=true;
|
||||
|
||||
}
|
||||
//Since the sending of data on the link is device
|
||||
//specific, return a non zero value to tell
|
||||
//the user code to issue a write of d->respstr
|
||||
int process_char(sr_device_t *d,char charin){
|
||||
int tmpint,tmpint2,ret;
|
||||
//set default rspstr for all commands that have a dataless ack
|
||||
d->rspstr[0]='*';
|
||||
d->rspstr[1]=0;
|
||||
//the reset character works by itself
|
||||
if(charin=='*'){
|
||||
reset(d);
|
||||
Dprintf("RST* sending %d\n\r",d->sending);
|
||||
return 0;
|
||||
}else if((charin=='\r')||(charin=='\n')){
|
||||
d->cmdstr[d->cmdstrptr]=0;
|
||||
switch(d->cmdstr[0]){
|
||||
case 'i':
|
||||
//SREGEN,AxxyDzz,00 - num analog, analog size, num digital,version
|
||||
sprintf(d->rspstr,"SRGEN,A%02d1D%02d,00\n",NUM_A_CHAN,NUM_D_CHAN);
|
||||
Dprintf("ID rsp %s\n\r",d->rspstr);
|
||||
ret=1;
|
||||
break;
|
||||
case 'R':
|
||||
tmpint=atol(&(d->cmdstr[1]));
|
||||
if((tmpint>0)&&(tmpint<=MAX_SAMPLE_RATE)){
|
||||
d->sample_rate=tmpint;
|
||||
Dprintf("SMPRATE= %u\n\r",d->sample_rate);
|
||||
ret=1;
|
||||
}
|
||||
else {
|
||||
Dprintf("unsupported smp rate %s\n\r",d->cmdstr);
|
||||
ret=0;
|
||||
}
|
||||
break;
|
||||
//sample limit
|
||||
case 'L':
|
||||
tmpint=atol(&(d->cmdstr[1]));
|
||||
if(tmpint>0){
|
||||
d->num_samples=tmpint;
|
||||
Dprintf("NUMSMP=%u",d->num_samples);
|
||||
ret=1;
|
||||
}else{
|
||||
Dprintf("unsupported num samples %s\n\r",d->cmdstr);
|
||||
ret=0;
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
tmpint=atoi(&(d->cmdstr[1])); //extract channel number
|
||||
if(tmpint>=0){
|
||||
sprintf(d->rspstr,"0.0257,0.0\n"); //3.3/(2^7) and 0V offset
|
||||
Dprintf("ASCL%d\n\r",tmpint);
|
||||
ret=1;
|
||||
}else{
|
||||
Dprintf("bad ascale %s\n\r",d->cmdstr);
|
||||
ret=1; //this will return a '*' causing the host to fail
|
||||
}
|
||||
break;
|
||||
case 'F': //fixed set of samples
|
||||
Dprintf("starting fixed dump\n\r");
|
||||
tx_init(d);
|
||||
d->cont=0;
|
||||
ret=0;
|
||||
break;
|
||||
case 'C': //continous mode
|
||||
tx_init(d);
|
||||
d->cont=1;
|
||||
Dprintf("starting continous stream\n\r");
|
||||
ret=0;
|
||||
break;
|
||||
//format is Axyy where x is 0 for disabled, 1 for enabled and yy is channel #
|
||||
case 'A': ///enable analog channel always a set
|
||||
tmpint=d->cmdstr[1]-'0'; //extract enable value
|
||||
tmpint2=atoi(&(d->cmdstr[2])); //extract channel number
|
||||
if((tmpint>=0)&&(tmpint<=1)&&(tmpint2>=0)&&(tmpint2<=31)){
|
||||
d->a_mask=d->a_mask & ~(1<<tmpint2);
|
||||
d->a_mask=d->a_mask | (tmpint<<tmpint2);
|
||||
Dprintf("A%d EN %d Msk 0x%X\n\r",tmpint2,tmpint,d->a_mask);
|
||||
ret=1;
|
||||
}else{
|
||||
ret=0;
|
||||
}
|
||||
break;
|
||||
//format is Dxyy where x is 0 for disabled, 1 for enabled and yy is channel #
|
||||
case 'D': ///enable digital channel always a set
|
||||
tmpint=d->cmdstr[1]-'0'; //extract enable value
|
||||
tmpint2=atoi(&(d->cmdstr[2])); //extract channel number
|
||||
if((tmpint>=0)&&(tmpint<=1)&&(tmpint2>=0)&&(tmpint2<=31)){
|
||||
d->d_mask=d->d_mask & ~(1<<tmpint2);
|
||||
d->d_mask=d->d_mask | (tmpint<<tmpint2);
|
||||
Dprintf("D%d EN %d Msk 0x%X\n\r",tmpint2,tmpint,d->d_mask);
|
||||
ret=1;
|
||||
}else{
|
||||
ret=0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Dprintf("bad command %s\n\r",d->cmdstr);
|
||||
ret=0;
|
||||
}//case
|
||||
// Dprintf("CmdDone %s\n\r",d->cmdstr);
|
||||
d->cmdstrptr=0;
|
||||
}else{ //no CR/LF
|
||||
if(d->cmdstrptr>=19){
|
||||
d->cmdstr[18]=0;
|
||||
Dprintf("Command overflow %s\n\r",d->cmdstr);
|
||||
d->cmdstrptr=0;
|
||||
}
|
||||
d->cmdstr[d->cmdstrptr++]=charin;
|
||||
ret=0;
|
||||
}//else
|
||||
//default return 0 means to not send any kind of response
|
||||
return ret;
|
||||
}//process_char
|
||||
|
||||
|
||||
604
sigrok-generic/api.c
Normal file
604
sigrok-generic/api.c
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2018 mhooijboer <marchelh@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//debug print levels are err/warn/info/dbg/spew
|
||||
#include <config.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <libsigrok/libsigrok.h>
|
||||
#include "libsigrok-internal.h"
|
||||
#include "protocol.h"
|
||||
|
||||
|
||||
#define SERIALCOMM "115200/8n1"
|
||||
|
||||
static const uint32_t scanopts[] = {
|
||||
SR_CONF_CONN, //Required OS name for the port, i.e. /dev/ttyACM0
|
||||
SR_CONF_SERIALCOMM, //Optional config of the port, i.e. 115200/8n1
|
||||
};
|
||||
|
||||
//PulseView reads a sample rate config list as a min, max and step.
|
||||
//If step is 1 then it creates a 1,2,5,10 set of selects, as well as the max.
|
||||
//If step is not 1, then it gives a place to enter any value, which gives the greatest flexibility
|
||||
static const uint64_t samplerates[] = {
|
||||
SR_HZ(10),
|
||||
SR_MHZ(120),
|
||||
SR_HZ(2),
|
||||
};
|
||||
|
||||
static const uint32_t drvopts[] = {
|
||||
SR_CONF_OSCILLOSCOPE,
|
||||
SR_CONF_LOGIC_ANALYZER,
|
||||
};
|
||||
//SW trigger requires this
|
||||
static const int32_t trigger_matches[] = {
|
||||
SR_TRIGGER_ZERO,
|
||||
SR_TRIGGER_ONE,
|
||||
SR_TRIGGER_RISING,
|
||||
SR_TRIGGER_FALLING,
|
||||
SR_TRIGGER_EDGE,
|
||||
};
|
||||
|
||||
|
||||
static const uint32_t devopts[] = {
|
||||
//CLI prefers LIMIT_SAMPLES to be a list of high,low
|
||||
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||
SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
|
||||
SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
|
||||
//pulseview needs a list return to allow sample rate setting
|
||||
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
|
||||
};
|
||||
|
||||
static struct sr_dev_driver sr_generic_driver_info;
|
||||
|
||||
|
||||
static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
||||
{
|
||||
struct sr_config *src;
|
||||
struct sr_dev_inst *sdi;
|
||||
struct sr_serial_dev_inst *serial;
|
||||
struct dev_context *devc;
|
||||
struct sr_channel *ch;
|
||||
GSList *l;
|
||||
int num_read;
|
||||
unsigned int i;
|
||||
const char *conn, *serialcomm;
|
||||
char buf[32];
|
||||
int len;
|
||||
uint8_t num_a,num_d,a_size;
|
||||
gchar *channel_name;
|
||||
|
||||
conn = serialcomm = NULL;
|
||||
for (l = options; l; l = l->next) {
|
||||
src = l->data;
|
||||
switch (src->key) {
|
||||
case SR_CONF_CONN:
|
||||
conn = g_variant_get_string(src->data, NULL);
|
||||
break;
|
||||
case SR_CONF_SERIALCOMM:
|
||||
serialcomm = g_variant_get_string(src->data, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (!serialcomm)
|
||||
serialcomm = SERIALCOMM;
|
||||
|
||||
serial = sr_serial_dev_inst_new(conn, serialcomm);
|
||||
sr_info("Opening %s.", conn);
|
||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK){
|
||||
sr_err("1st serial open fail");
|
||||
return NULL;
|
||||
}
|
||||
sr_info("Reseting device with *s at %s.", conn);
|
||||
send_serial_char(serial,'*');
|
||||
g_usleep(10000);
|
||||
//drain any inflight data
|
||||
do{
|
||||
sr_warn("Drain reads");
|
||||
len=serial_read_blocking(serial, buf,32,100);
|
||||
sr_warn("Drain reads done");
|
||||
if(len) sr_dbg("Dropping in flight serial data");
|
||||
}while(len>0);
|
||||
|
||||
|
||||
//Send identify
|
||||
num_read=send_serial_w_resp(serial,"i\n",buf,20);
|
||||
if(num_read<16){
|
||||
sr_err("1st identify failed");
|
||||
serial_close(serial);
|
||||
g_usleep(100000);
|
||||
if (serial_open(serial, SERIAL_RDWR) != SR_OK){
|
||||
sr_err("2st serial open fail");
|
||||
return NULL;
|
||||
}
|
||||
g_usleep(100000);
|
||||
sr_err("Send second *");
|
||||
send_serial_char(serial,'*');
|
||||
g_usleep(100000);
|
||||
num_read=send_serial_w_resp(serial,"i\n",buf,20);
|
||||
if(num_read<10){
|
||||
sr_err("Second attempt failed");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
//Expected ID response is SRGEN,AxxyDzz,VV
|
||||
//where xx are number of analog channels, y is bytes per analog sample
|
||||
//and zz is number of digital channels, and VV is two digit version# which must be 00
|
||||
if((num_read<16)
|
||||
||(strncmp(buf,"SRGEN,A",7))
|
||||
||(buf[10]!='D')
|
||||
||(buf[14]!='0')
|
||||
||(buf[15]!='0')){
|
||||
sr_err("ERROR:Bad response string %s %d",buf,num_read);
|
||||
return NULL;
|
||||
}
|
||||
a_size=buf[9]-'0';
|
||||
buf[9]='\0'; //Null to end the str for atois
|
||||
buf[13]='\0'; //Null to end the str for atois
|
||||
num_a=atoi(&buf[7]);
|
||||
num_d=atoi(&buf[11]);
|
||||
|
||||
|
||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||
sdi->status = SR_ST_INACTIVE;
|
||||
sdi->vendor = g_strdup("Sigrok");
|
||||
sdi->model = g_strdup("Generic");
|
||||
sdi->version = g_strdup("00");
|
||||
sdi->conn = serial;
|
||||
//broken/fixme
|
||||
// sdi->driver = &sr_generic_driver_info;
|
||||
sdi->inst_type = SR_INST_SERIAL;
|
||||
sdi->serial_num = g_strdup("N/A");
|
||||
if(((num_a==0)&&(num_d==0))
|
||||
||(num_a>MAX_ANALOG_CHANNELS)
|
||||
||(num_d>MAX_DIGITAL_CHANNELS)
|
||||
||(a_size<1)
|
||||
||(a_size>4)){
|
||||
sr_err("ERROR: invalid channel config a %d d %d asz %d",num_a,num_d,a_size);
|
||||
return NULL;
|
||||
}
|
||||
devc = g_malloc0(sizeof(struct dev_context));
|
||||
//TODO- deprecate a_size
|
||||
devc->a_size=a_size;
|
||||
//multiple bytes per analog sample not supported
|
||||
if((num_a>0)&&(devc->a_size!=1)){
|
||||
sr_err("Only Analog Size of 1 supported\n\r");
|
||||
return NULL;
|
||||
}
|
||||
devc->num_a_channels=num_a;
|
||||
devc->num_d_channels=num_d;
|
||||
devc->a_chan_mask=((1<<num_a)-1);
|
||||
devc->d_chan_mask=((1<<num_d)-1);
|
||||
//The number of bytes that each digital sample in the buffers sent to the session.
|
||||
//All logical channels are packed together, where a slice of N channels takes roundup(N/8) bytes
|
||||
//This never changes even if channels are disabled because PV expects disabled channels to still
|
||||
//be accounted for in the packing
|
||||
//this ran forever but looks wrong... 16 channels would say 3 bytes
|
||||
// devc->dig_sample_bytes=((devc->num_d_channels/8)+1);
|
||||
devc->dig_sample_bytes=(devc->num_d_channels/8);
|
||||
if((devc->num_d_channels)&0x7) devc->dig_sample_bytes++;
|
||||
//These are the slice sizes of the data on the wire
|
||||
//1 7 bit field per byte
|
||||
devc->bytes_per_slice=(devc->num_a_channels*devc->a_size);
|
||||
if(devc->num_d_channels>0){
|
||||
// logic sent in groups of 7
|
||||
devc->bytes_per_slice+=(devc->num_d_channels+6)/7;
|
||||
}
|
||||
sr_dbg("num channels a %d d %d bps %d dsb %d",num_a,num_d,devc->bytes_per_slice,devc->dig_sample_bytes);
|
||||
//Each analog channel is it's own grup
|
||||
//Digital are just channels
|
||||
//Grouping of channels is rather arbitrary as parameters like sample rate and number of samples
|
||||
//apply to all changes. Analog channels do have a scale and offset, but that it does
|
||||
//without involvement of the session.
|
||||
devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group *) *
|
||||
devc->num_a_channels);
|
||||
for (i = 0; i < devc->num_a_channels; i++) {
|
||||
channel_name = g_strdup_printf("A%d", i );
|
||||
//sdi, index, type, enabled,name
|
||||
ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name);
|
||||
devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
|
||||
devc->analog_groups[i]->name = channel_name;
|
||||
devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
|
||||
sdi->channel_groups = g_slist_append(sdi->channel_groups,
|
||||
devc->analog_groups[i]);
|
||||
}
|
||||
|
||||
if (devc->num_d_channels>0) {
|
||||
for (i = 0; i < devc->num_d_channels; i++){
|
||||
channel_name = g_strdup_printf("D%d", i);
|
||||
sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE,
|
||||
channel_name);
|
||||
g_free(channel_name);
|
||||
}
|
||||
|
||||
}
|
||||
//In large sample usages we get the call to receive with large transfers.
|
||||
//Since the CDC serial implemenation can silenty lose data as it gets close to full, allocate
|
||||
//storage for a half buffer which in a worst case scenario has 2x ratio of transmitted bytes to storage bytes
|
||||
devc->serial_buffer_size=256000;
|
||||
devc->buffer=NULL;
|
||||
sr_dbg("Setting serial buffer size: %i.", devc->serial_buffer_size);
|
||||
devc->cbuf_wrptr=0;
|
||||
//While slices are sent as a group of one sample across all channels, sigrok wants analog
|
||||
//channel data sent as separate packets.
|
||||
//Logical trace values are packed together.
|
||||
//A serial byte can never represent more than one sample, but allocate 2x the space anyway.
|
||||
devc->sample_buf_size=devc->serial_buffer_size*2;
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
devc->a_data_bufs[i]=NULL;
|
||||
devc->a_pretrig_bufs[i]=NULL;
|
||||
}
|
||||
devc->d_data_buf=NULL;
|
||||
devc->sample_rate=1000;
|
||||
devc->capture_ratio=10;
|
||||
devc->rxstate=RX_IDLE;
|
||||
sdi->priv = devc;
|
||||
//Set an initial value as various code relies on an inital value.
|
||||
devc->limit_samples=1000;
|
||||
|
||||
//TODO - check return code,though I might just eliminate this function and add it's code here
|
||||
sr_generic_get_dev_cfg(sdi);
|
||||
|
||||
sr_err("sr_err level logging enabled");
|
||||
sr_warn("sr_warn level logging enabled");
|
||||
sr_info("sr_info level logging enabled");
|
||||
sr_dbg("sr_dbg level logging enabled");
|
||||
sr_spew("sr_spew level logging enabled");
|
||||
//TODO - ols closes the serial port here - should I?
|
||||
return std_scan_complete(di, g_slist_append(NULL, sdi));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Note that on the initial driver load we pull all values into local storage.
|
||||
//Thus gets can return local data, but sets have to issue commands to device.
|
||||
static int config_set(uint32_t key, GVariant *data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
int ret;
|
||||
(void)cg;
|
||||
if (!sdi)
|
||||
return SR_ERR_ARG;
|
||||
devc=sdi->priv;
|
||||
ret = SR_OK;
|
||||
sr_dbg("Got config_set key %d \n",key);
|
||||
switch (key) {
|
||||
case SR_CONF_SAMPLERATE:
|
||||
devc->sample_rate = g_variant_get_uint64(data);
|
||||
sr_dbg("config_set sr %llu\n",devc->sample_rate);
|
||||
break;
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
devc->limit_samples = g_variant_get_uint64(data);
|
||||
sr_dbg("config_set slimit %lld\n",devc->limit_samples);
|
||||
break;
|
||||
case SR_CONF_CAPTURE_RATIO:
|
||||
devc->capture_ratio = g_variant_get_uint64(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
sr_err("ERROR:config_set undefine %d\n",key);
|
||||
ret = SR_ERR_NA;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int config_get(uint32_t key, GVariant **data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
sr_dbg("at config_get key %d",key);
|
||||
(void)cg;
|
||||
if (!sdi)
|
||||
return SR_ERR_ARG;
|
||||
|
||||
devc = sdi->priv;
|
||||
switch (key) {
|
||||
case SR_CONF_SAMPLERATE:
|
||||
*data = g_variant_new_uint64(devc->sample_rate);
|
||||
sr_spew("sample rate get of %lld",devc->sample_rate);
|
||||
break;
|
||||
case SR_CONF_CAPTURE_RATIO:
|
||||
if (!sdi)
|
||||
return SR_ERR;
|
||||
devc = sdi->priv;
|
||||
*data = g_variant_new_uint64(devc->capture_ratio);
|
||||
break;
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
sr_spew("config_get limit_samples of %llu",devc->limit_samples);
|
||||
*data = g_variant_new_uint64(devc->limit_samples);
|
||||
break;
|
||||
default:
|
||||
sr_spew("unsupported cfg_get key %d",key);
|
||||
return SR_ERR_NA;
|
||||
}
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int config_list(uint32_t key, GVariant **data,
|
||||
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
|
||||
{
|
||||
(void)cg;
|
||||
//scan or device options are the only ones that can be called without a defined instance
|
||||
if((key==SR_CONF_SCAN_OPTIONS)||(key==SR_CONF_DEVICE_OPTIONS)){
|
||||
return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
|
||||
}
|
||||
if (!sdi){
|
||||
sr_err("ERROR:\n\r\n\r\n\r Call to config list with null sdi\n\r\n\r");
|
||||
return SR_ERR_ARG;
|
||||
}
|
||||
sr_dbg("start config_list with key %X\n",key);
|
||||
switch(key){
|
||||
//Pulseview in pulseview/pv/toolbars/mainbar.cpp requires list support for frequencies as a triple
|
||||
//of min,max,step. If step is 1, then it proves a 1,2,5,10 select, but if not 1 it allows a spin box
|
||||
case SR_CONF_SAMPLERATE:
|
||||
sr_dbg("Return sample rate list");
|
||||
*data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates));
|
||||
break;
|
||||
//This must be set to get SW trigger support
|
||||
case SR_CONF_TRIGGER_MATCH:
|
||||
*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
|
||||
break;
|
||||
case SR_CONF_LIMIT_SAMPLES:
|
||||
//Really this limit is up to the memory capacity of the host,
|
||||
//and users that pick huge values deserve what they get.
|
||||
//But setting this limit to prevent really crazy things.
|
||||
*data = std_gvar_tuple_u64(1LL,1000000000LL);
|
||||
sr_dbg("sr_config_list limit samples ");
|
||||
break;
|
||||
default:
|
||||
sr_dbg("reached default statement of config_list");
|
||||
|
||||
return SR_ERR_NA;
|
||||
}
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct sr_serial_dev_inst *serial;
|
||||
struct dev_context *devc;
|
||||
struct sr_channel *ch;
|
||||
struct sr_trigger *trigger;
|
||||
char tmpstr[20];
|
||||
GSList *l;
|
||||
int a_enabled=0,d_enabled=0,len;
|
||||
//TODO - downgrade all the sr_err that are not errors once this works
|
||||
serial = sdi->conn;
|
||||
int i;
|
||||
devc = sdi->priv;
|
||||
sr_dbg("Enter acq start");
|
||||
sr_dbg("dsbstart %d",devc->dig_sample_bytes);
|
||||
devc->buffer = g_malloc(devc->serial_buffer_size);
|
||||
if(!(devc->buffer)){sr_err("ERROR:serial buffer malloc fail");return SR_ERR_MALLOC;}
|
||||
|
||||
//Get device in idle state
|
||||
if(serial_drain(serial)!=SR_OK){sr_err("Initial Drain Failed\n\r");return SR_ERR;}
|
||||
send_serial_char(serial,'*');
|
||||
if(serial_drain(serial)!=SR_OK){sr_err("Second Drain Failed\n\r");return SR_ERR;}
|
||||
|
||||
sprintf(tmpstr,"L%lld\n", devc->limit_samples);
|
||||
if(send_serial_w_ack(serial, tmpstr)!=SR_OK) {
|
||||
sr_err("Sample limit to device failed");
|
||||
return SR_ERR;
|
||||
}
|
||||
sprintf(&tmpstr[0],"R%llu\n", devc->sample_rate);
|
||||
if(send_serial_w_ack(serial, tmpstr)!=SR_OK) {
|
||||
sr_err("Sample rate to device failed");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
for (l = sdi->channels; l; l = l->next) {
|
||||
ch = l->data;
|
||||
sr_dbg("c %d enabled %d name %s\n",ch->index,ch->enabled,ch->name);
|
||||
|
||||
if(ch->name[0]=='A'){
|
||||
devc->a_chan_mask&=~(1<<ch->index);
|
||||
if(ch->enabled) {
|
||||
devc->a_chan_mask|=(ch->enabled<<ch->index);
|
||||
a_enabled++;
|
||||
}
|
||||
sr_dbg("A%d en %d mask 0x%X",ch->index,ch->enabled,devc->a_chan_mask);
|
||||
|
||||
}
|
||||
if(ch->name[0]=='D'){
|
||||
devc->d_chan_mask&=~(1<<ch->index);
|
||||
if(ch->enabled) {
|
||||
devc->d_chan_mask|=(ch->enabled<<ch->index);
|
||||
d_enabled++;
|
||||
}
|
||||
sr_dbg("D%d en %d mask 0x%X",ch->index,ch->enabled,devc->d_chan_mask);
|
||||
}
|
||||
sprintf(tmpstr,"%c%d%d\n",ch->name[0],ch->enabled,ch->index);
|
||||
//TODO - do something with the error
|
||||
if (send_serial_w_ack(serial,tmpstr) != SR_OK){
|
||||
sr_err("ERROR:Channel enable fail");
|
||||
} else{
|
||||
}
|
||||
}//for all channels
|
||||
//recalculate bytes_per_slice.
|
||||
devc->bytes_per_slice=(a_enabled*devc->a_size);
|
||||
|
||||
for(i=0;i<devc->num_d_channels;i+=7){
|
||||
if(((devc->d_chan_mask)>>i)&(0x7F)){(devc->bytes_per_slice)++;}
|
||||
}
|
||||
|
||||
sr_dbg("bps %d\n",devc->bytes_per_slice);
|
||||
devc->sent_samples=0;
|
||||
devc->byte_cnt=0;
|
||||
devc->bytes_avail=0;
|
||||
devc->wrptr=0;
|
||||
devc->cbuf_wrptr=0;
|
||||
len=serial_read_blocking(serial, devc->buffer, devc->serial_buffer_size,serial_timeout(serial, 4));
|
||||
if(len>0){
|
||||
sr_info("Pre-ARM drain had %d characters:",len);
|
||||
devc->buffer[len]=0;
|
||||
sr_info("%s",devc->buffer);
|
||||
}
|
||||
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
devc->a_data_bufs[i]=g_malloc(devc->sample_buf_size*sizeof(float));
|
||||
if(!(devc->a_data_bufs[i])){sr_err("ERROR:analog buffer malloc fail");return SR_ERR_MALLOC;}
|
||||
}
|
||||
if(devc->num_d_channels>0){
|
||||
devc->d_data_buf=g_malloc(devc->sample_buf_size*devc->dig_sample_bytes);
|
||||
if(!(devc->d_data_buf)){sr_err("ERROR:logic buffer malloc fail");return SR_ERR_MALLOC;}
|
||||
}
|
||||
|
||||
//TODO-understand these params
|
||||
if ((trigger = sr_session_trigger_get(sdi->session))) {
|
||||
devc->pretrig_entries = (devc->capture_ratio * devc->limit_samples) / 100;
|
||||
devc->stl = soft_trigger_logic_new(sdi, trigger, devc->pretrig_entries);
|
||||
if (!devc->stl)
|
||||
return SR_ERR_MALLOC;
|
||||
devc->trigger_fired=FALSE;
|
||||
if(devc->pretrig_entries>0){
|
||||
sr_dbg("Allocating pretrig buffers size %d",devc->pretrig_entries);
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
//TODO-might look for the pointers being not null?
|
||||
devc->a_pretrig_bufs[i] = g_malloc0(sizeof(float)*devc->pretrig_entries);
|
||||
if(!devc->a_pretrig_bufs[i]){
|
||||
sr_err("ERROR:Analog pretrigger buffer malloc failure, disabling");
|
||||
devc->trigger_fired=TRUE;
|
||||
}
|
||||
}//if chan_mask
|
||||
}//for num_a_channels
|
||||
}//if pre_trigger
|
||||
|
||||
|
||||
sr_info("Entering sw triggered mode");
|
||||
//post the receive before starting the device to ensure we are ready to receive data ASAP
|
||||
//TODO-Jan/1 I had this at 7000 for scpi version but most serial uses are in the 50-200 range
|
||||
//note that I call it twice (just below)
|
||||
serial_source_add(sdi->session, serial, G_IO_IN, 200,sr_generic_receive, (void *) sdi);
|
||||
sprintf(tmpstr,"C\n");
|
||||
if(send_serial_str(serial, tmpstr) != SR_OK)
|
||||
return SR_ERR;
|
||||
|
||||
} else{
|
||||
devc->trigger_fired=TRUE;
|
||||
devc->pretrig_entries=0;
|
||||
sr_info("Entering fixed sample mode");
|
||||
serial_source_add(sdi->session, serial, G_IO_IN, 200,sr_generic_receive, (void *) sdi);
|
||||
sprintf(tmpstr,"F\n");
|
||||
if(send_serial_str(serial,tmpstr) != SR_OK)
|
||||
return SR_ERR;
|
||||
}
|
||||
std_session_send_df_header(sdi);
|
||||
|
||||
sr_dbg("dsbstartend %d",devc->dig_sample_bytes);
|
||||
|
||||
if(devc->trigger_fired) std_session_send_df_trigger(sdi);
|
||||
//Keep this at the end as we don't want to be RX_ACTIVE unless everything is ok
|
||||
devc->rxstate=RX_ACTIVE;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
//TODO-it's not clear how this is a clean stop if we are in the middle of an acquisition
|
||||
//as the generic_receive may still be running and it's not clear how the device gets reinited
|
||||
//part of the answer may be that we get a special event sent to the generic_receiver???
|
||||
//though protocol.c references the enabled channels without checking for it becoming null..
|
||||
//also need to review to make sure any mallocs in acquisition start are freed
|
||||
|
||||
//TODO - answer to above seems that we can hit this stop if the user hits stop in the pulseview app
|
||||
//(perhaps a runaway sample), but we also need to always call this on the completion of any transfer
|
||||
//because we free memory of the serail buffer and other things
|
||||
|
||||
static int dev_acquisition_stop(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
struct sr_serial_dev_inst *serial;
|
||||
sr_dbg("****at dev_acquisition_stop");
|
||||
int len;
|
||||
devc = sdi->priv;
|
||||
serial = sdi->conn;
|
||||
|
||||
std_session_send_df_end(sdi);
|
||||
//If we reached this while still active it is likely because the stop button was pushed in pulseview.
|
||||
//That normally should be some kind of error condition, so we don't try to check the bytenct
|
||||
if(devc->rxstate==RX_ACTIVE){
|
||||
sr_err("Reached dev_acquisition_stop in RX_ACTIVE");
|
||||
//In case we get calls to generic_receive force it to exit
|
||||
devc->rxstate=RX_IDLE;
|
||||
}
|
||||
|
||||
//TODO- a reset '*' may be a bit too much, and the + likely is responded too quicker
|
||||
if(devc->rxstate!=RX_IDLE){
|
||||
sr_err("Sending plus to stop device stream\n\r");
|
||||
send_serial_char(serial,'+');
|
||||
}
|
||||
//drain data from device so that it doesn't confuse subsequent commands
|
||||
do{
|
||||
len=serial_read_blocking(serial, devc->buffer, devc->serial_buffer_size,100);
|
||||
if(len) sr_err("Dropping %d device bytes\n\r",len);
|
||||
}while(len>0);
|
||||
|
||||
|
||||
|
||||
if(devc->buffer){g_free(devc->buffer);devc->buffer=NULL;}
|
||||
|
||||
for(int i=0;i<devc->num_a_channels;i++){
|
||||
if(devc->a_data_bufs[i]){
|
||||
g_free(devc->a_data_bufs[i]);
|
||||
devc->a_data_bufs[i]=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(devc->d_data_buf){g_free(devc->d_data_buf);devc->d_data_buf=NULL;}
|
||||
for(int i=0;i<devc->num_a_channels;i++){
|
||||
if(devc->a_pretrig_bufs[i]) g_free(devc->a_pretrig_bufs[i]);
|
||||
devc->a_pretrig_bufs[i]=NULL;
|
||||
}
|
||||
|
||||
serial= sdi->conn;
|
||||
serial_source_remove(sdi->session, serial);
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static struct sr_dev_driver sr_generic_driver_info = {
|
||||
.name = "sigrok-generic",
|
||||
.longname = "Sigrok Generic",
|
||||
.api_version = 1,
|
||||
.init = std_init,
|
||||
.cleanup = std_cleanup,
|
||||
.scan = scan,
|
||||
.dev_list = std_dev_list,
|
||||
.dev_clear = std_dev_clear,
|
||||
.config_get = config_get,
|
||||
.config_set = config_set,
|
||||
.config_list = config_list,
|
||||
.dev_open = std_serial_dev_open,
|
||||
.dev_close = std_serial_dev_close,
|
||||
.dev_acquisition_start = dev_acquisition_start,
|
||||
.dev_acquisition_stop = dev_acquisition_stop,
|
||||
.context = NULL,
|
||||
};
|
||||
SR_REGISTER_DEV_DRIVER(sr_generic_driver_info);
|
||||
797
sigrok-generic/protocol.c
Normal file
797
sigrok-generic/protocol.c
Normal file
|
|
@ -0,0 +1,797 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2016 mhooijboer <marchelh@gmail.com>
|
||||
* Copyright (C) 2012 Martin Ling <martin-git@earth.li>
|
||||
* Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
|
||||
* Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <libsigrok/libsigrok.h>
|
||||
#include "libsigrok-internal.h"
|
||||
#include "protocol.h"
|
||||
|
||||
|
||||
SR_PRIV int send_serial_str(struct sr_serial_dev_inst *serial, char *str){
|
||||
int len=strlen(str);
|
||||
if((len>15)||(len<1)){ //limit length to catch errant strings
|
||||
sr_err("ERROR:Serial string len %d invalid ",len);
|
||||
return SR_ERR;
|
||||
}
|
||||
if(serial_write_blocking(serial,str,len,serial_timeout(serial, 1)) != len){
|
||||
sr_err("ERROR:Serial str write failed");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
//TODO serial drains take a while and may not add much
|
||||
// if (serial_drain(serial) != SR_OK){
|
||||
// sr_err("ERROR:Serial str drain failed");
|
||||
// return SR_ERR;
|
||||
// }
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
SR_PRIV int send_serial_char(struct sr_serial_dev_inst *serial, char ch){
|
||||
char buf[1];
|
||||
buf[0]=ch;
|
||||
if(serial_write_blocking(serial,buf,1,serial_timeout(serial, 1)) != 1){
|
||||
sr_err("ERROR:Serial char write failed");
|
||||
return SR_ERR;
|
||||
}
|
||||
//TODO serial drains take a while and may not add much
|
||||
/*
|
||||
if (serial_drain(serial) != SR_OK){
|
||||
sr_err("ERROR:Serial char drain failed");
|
||||
return SR_ERR;
|
||||
}
|
||||
*/
|
||||
return SR_OK;
|
||||
}
|
||||
//Issue a command that expects a string return, return length of string
|
||||
int send_serial_w_resp(struct sr_serial_dev_inst *serial, char *str,char *resp,size_t cnt){
|
||||
int num_read,i;
|
||||
send_serial_str(serial,str);
|
||||
//Using the serial_read_blocking function when reading a response of unknown length requires
|
||||
//a long worst case timeout to always be taken. So, instead loop waiting for a first byte, and
|
||||
//then a final small delay for the rest.
|
||||
for(i=0;i<1000;i++){
|
||||
num_read = serial_read_blocking(serial, resp, cnt, 1);
|
||||
if(num_read>0) break;
|
||||
}
|
||||
sr_spew("rwprsp1 i %d nr %d",i,num_read);
|
||||
//A typical 20char response at 115kbaud is ~1ms after the first char so 10ms is plenty
|
||||
num_read+= serial_read_blocking(serial, &(resp[num_read]), cnt-num_read, 10);
|
||||
sr_spew("rwrsp2 nr %d",num_read);
|
||||
|
||||
if ((num_read < 1)||(num_read>30)) {
|
||||
sr_err("ERROR:Serial_w_resp try 1 failed (%d).", num_read);
|
||||
return -1;
|
||||
}else{
|
||||
return num_read;
|
||||
}
|
||||
//TODO cleanup
|
||||
/*
|
||||
num_read = serial_read_blocking(serial, resp, cnt, 100);
|
||||
if ((num_read < 1)||(num_read>30)) {
|
||||
sr_err("ERROR:Serial_w_resp try 2 failed (%d).", num_read);
|
||||
return -1;
|
||||
}
|
||||
return num_read;
|
||||
*/
|
||||
}
|
||||
//Issue a command that expects a single char ack
|
||||
SR_PRIV int send_serial_w_ack(struct sr_serial_dev_inst *serial, char *str){
|
||||
char buf[2];
|
||||
int num_read;
|
||||
while(num_read =serial_read_blocking(serial, buf, 2, 10)){
|
||||
sr_dbg("swack drops 2 previous bytes %d %d",buf[0],buf[1]);
|
||||
}
|
||||
send_serial_str(serial,str);
|
||||
//1000ms timeout
|
||||
num_read =serial_read_blocking(serial, buf, 1, 1000);
|
||||
if ((num_read == 1)&&(buf[0]=='*')) {
|
||||
return SR_OK;
|
||||
}else{
|
||||
sr_err("ERROR:Serial_w_ack %s failed (%d).", str,num_read);
|
||||
if(num_read){
|
||||
sr_err("ack resp char %c d %d\n\r",buf[0],buf[0]);
|
||||
}
|
||||
return SR_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
//Process incoming data stream assuming it is optimized packing of 4 channels (or less)
|
||||
//Each byte is 4 channels of data and a 3 bit rle value, or a larger rle value, or a control signal.
|
||||
//This also checks for aborts and ends.
|
||||
//If an end is seen we stop processing but do not check the byte_cnt
|
||||
//The output is a set of samples fed to process group to perform sw triggering and sending of data to the session
|
||||
//as well as maintenance of the serial rx byte cnt.
|
||||
//Since we can get huge rle values we chop them up for processing into smaller groups
|
||||
void process_D4(struct sr_dev_inst *sdi,struct dev_context *d){
|
||||
int32_t j;
|
||||
uint8_t cbyte;
|
||||
uint8_t cval;
|
||||
uint32_t rlecnt=0,rlerem,rlecopied;
|
||||
uint32_t sampcnt=0; //number of samples received with no rles
|
||||
//In 4 channel mode we can always consume all bytes because there are no cases where the processing of one
|
||||
//byte requires another.
|
||||
//COvering the sr_ prints in case they cause perf overhead issues even when not used
|
||||
#ifdef D4_DBG
|
||||
sr_dbg("pd4 %d %d %d",d->ser_rdptr,d->wrptr,d->bytes_avail);
|
||||
#endif
|
||||
//TODO the use of ser_rdptr is likely redundant as I thik it should always zero on entry
|
||||
//and thus the wile loop might instead be for loop setting rdptr to 0 and going to bytes avail?
|
||||
#ifdef D4_DBG
|
||||
if(d->ser_rdptr){
|
||||
sr_err("serial read pointer %d not zero",d->ser_rdptr);
|
||||
}
|
||||
#endif
|
||||
while((d->ser_rdptr)<(d->bytes_avail)){
|
||||
cbyte=d->buffer[(d->ser_rdptr)];
|
||||
#ifdef D4_DBG
|
||||
sr_spew("cbyte 0x%X rdptr %d",cbyte,d->ser_rdptr); //rle only
|
||||
#endif
|
||||
if((cbyte>=48)&&(cbyte<=79)){
|
||||
rlecnt+=(cbyte-47)*8;
|
||||
d->byte_cnt++;
|
||||
#ifdef D4_DBG
|
||||
sr_spew("Rle only byte 0x%X rlenct %d",cbyte,rlecnt);
|
||||
#endif
|
||||
}else if(cbyte>=0x80){ //sample with possible rle
|
||||
rlecnt+=(cbyte&0x70)>>4;
|
||||
|
||||
//We queue up large numbers of samples if they don't have rle, and then flush them
|
||||
//once we get an rle
|
||||
if(sampcnt&&rlecnt){
|
||||
#ifdef D4_DBG
|
||||
sr_spew("Pushing %d samples ahead of rlecnt %d",sampcnt,rlecnt);
|
||||
#endif
|
||||
process_group(sdi,d,sampcnt);
|
||||
sampcnt=0;
|
||||
}
|
||||
if(rlecnt){
|
||||
#ifdef D4_DBG
|
||||
sr_spew("Copy %d rles from sample byte",rlecnt);
|
||||
#endif
|
||||
rlerem=rlecnt;
|
||||
while(rlerem){
|
||||
rlecopied=rle_memset(d,rlerem);
|
||||
process_group(sdi,d,rlecopied);
|
||||
rlerem-=rlecopied;
|
||||
}
|
||||
rlecnt=0;
|
||||
}
|
||||
//In this mode we know that only ever set the LSByte and pad in up to dig_sample_bytes
|
||||
cval=cbyte&0xF;
|
||||
d->d_data_buf[d->cbuf_wrptr++]=cval;
|
||||
for(j=1;j<d->dig_sample_bytes;j++){
|
||||
d->d_data_buf[d->cbuf_wrptr++]=0;
|
||||
}
|
||||
sampcnt++;
|
||||
d->byte_cnt++;
|
||||
#ifdef D4_DBG
|
||||
sr_spew("Dchan4 rdptr %d wrptr %d bytein 0x%X rle %d cval 0x%X\n",
|
||||
(d->ser_rdptr)-1,d->cbuf_wrptr,cbyte,rlecnt,cval);
|
||||
#endif
|
||||
d->d_last[0]=cval;
|
||||
}
|
||||
//Any other character ends parsing - it could be a frame error or a start of the final byte cnt
|
||||
else {
|
||||
if(cbyte=='$'){
|
||||
sr_info("D4 Data stream stops with cbyte %d char %c rdidx %d cnt %llu",cbyte,cbyte,d->ser_rdptr,d->byte_cnt);
|
||||
d->rxstate=RX_STOPPED;
|
||||
}else{
|
||||
sr_err("D4 Data stream aborts with cbyte %d char %c rdidx %d cnt %llu",cbyte,cbyte,d->ser_rdptr,d->byte_cnt);
|
||||
d->rxstate=RX_ABORT;
|
||||
}
|
||||
break; //break from while loop
|
||||
}
|
||||
(d->ser_rdptr)++;
|
||||
}//while rdptr < wrptr
|
||||
//TODO - i no longer think this is an error. If we get a sample and then a bunch of rles after
|
||||
//it we can just do the sample and then the rles after it in the end.
|
||||
//we really only need to force a sample if it has rles before it and after it
|
||||
// if(sampcnt&&rlecnt){
|
||||
// sr_err("ERROR: Reached end of process_d4 with both samples %d and RLE %d",sampcnt,rlecnt);
|
||||
// }
|
||||
if(sampcnt){
|
||||
#ifdef D4_DBG
|
||||
sr_dbg("Process_d4 residual samples %d",sampcnt);
|
||||
#endif
|
||||
process_group(sdi,d,sampcnt);
|
||||
sampcnt=0;
|
||||
}
|
||||
if(rlecnt){
|
||||
#ifdef D4_DBG
|
||||
sr_info("Process_d4 residual rle %d",rlecnt);
|
||||
#endif
|
||||
rlerem=rlecnt;
|
||||
while(rlerem){
|
||||
rlecopied=rle_memset(d,rlerem);
|
||||
process_group(sdi,d,rlecopied);
|
||||
rlerem-=rlecopied;
|
||||
}
|
||||
rlecnt=0;
|
||||
}
|
||||
|
||||
}//Process_D4
|
||||
|
||||
//Process incoming data stream and forward to trigger processing with process_group
|
||||
//The final value of ser_rdptr indicates how many bytes were processed.
|
||||
//todo - review memory allocations to make sure target buffer can't overflow...
|
||||
|
||||
void process_slice(struct sr_dev_inst *sdi,struct dev_context *devc){
|
||||
int32_t i;
|
||||
uint32_t tmp32;
|
||||
uint8_t cbyte;
|
||||
uint32_t slices_avail=0;
|
||||
uint32_t cword;
|
||||
uint32_t slice_bytes; //number of bytes that have legal slice values
|
||||
|
||||
//Only process legal data values for this mode which are >=0x80
|
||||
for(slice_bytes=1;(slice_bytes<devc->bytes_avail)&&(devc->buffer[slice_bytes-1]>=0x80);slice_bytes++);
|
||||
|
||||
|
||||
if(slice_bytes!=devc->bytes_avail){
|
||||
cbyte=devc->buffer[slice_bytes-1];
|
||||
// slice_bytes-=2; //Don't process the ending character
|
||||
slice_bytes--;
|
||||
if(cbyte=='$'){
|
||||
sr_info("Data stream stops with cbyte %d char %c rdidx %d sbytes %d cnt %llu",cbyte,cbyte,devc->ser_rdptr,slice_bytes,devc->byte_cnt);
|
||||
devc->rxstate=RX_STOPPED;
|
||||
}else{
|
||||
sr_err("Data stream aborts with cbyte %d char %c rdidx %d sbytes %d cnt %llu",cbyte,cbyte,devc->ser_rdptr,slice_bytes,devc->byte_cnt);
|
||||
devc->rxstate=RX_ABORT;
|
||||
}
|
||||
|
||||
}
|
||||
//TODO - make sure this works (but if we don't see bytecnt errors it means it does :))
|
||||
//if the wrptr is non-zero due to a residual we don't want to double count it
|
||||
devc->byte_cnt+=slice_bytes-(devc->wrptr);
|
||||
sr_dbg("at process slice avail %d rdptr %d sb %d byte_cnt %d",devc->bytes_avail,devc->ser_rdptr,slice_bytes,devc->byte_cnt);
|
||||
//must have a full slice
|
||||
while((devc->ser_rdptr+devc->bytes_per_slice)<=slice_bytes){
|
||||
//The use of devc->cbuf_wrptr is different between analog and digital.
|
||||
//For analog it targets a float sized offset for that channel's buffer and thus
|
||||
//thus the analog channel size in bytes is sizeof(float)*max wrptr value
|
||||
//For digital it targets a bit, so the 3 lsbs are bit offsets within a byte
|
||||
//and thus a digital channel size is (max_wrptr value)/8
|
||||
sr_spew("Start slice");
|
||||
slices_avail++;
|
||||
cword=0;
|
||||
//build up a word 7 bits at a time, using only enabled channels
|
||||
for(i=0;i<devc->num_d_channels;i+=7){
|
||||
if(((devc->d_chan_mask)>>i)&0x7F){
|
||||
cword|=((devc->buffer[devc->ser_rdptr])&0x7F)<<i;
|
||||
sr_spew("Dchan in i %d rdptr %d char 0x%X cword 0x%X",i,devc->ser_rdptr,devc->buffer[devc->ser_rdptr],cword);
|
||||
(devc->ser_rdptr)++;
|
||||
}
|
||||
}
|
||||
//and then distribute up to the word 8 bits at a time to all possible channels
|
||||
for(i=0;i<devc->num_d_channels;i+=8){
|
||||
uint32_t idx=((devc->cbuf_wrptr)*devc->dig_sample_bytes)+(i>>3);
|
||||
devc->d_data_buf[idx]=cword&0xFF;
|
||||
sr_spew("Dchan out i %d wrptr %d idx %d char 0x%X cword 0x%X",i,devc->cbuf_wrptr,idx,devc->d_data_buf[idx],cword);
|
||||
cword>>=8;
|
||||
}
|
||||
//Each analog value is a 7 bit value
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
//a_size is depracted
|
||||
tmp32=devc->buffer[devc->ser_rdptr]-0x80;
|
||||
devc->a_data_bufs[i][devc->cbuf_wrptr]=((float)tmp32 * devc->a_scale[i])+devc->a_offset[i];
|
||||
devc->a_last[i]=devc->a_data_bufs[i][devc->cbuf_wrptr];
|
||||
sr_spew("AChan %d value %f ",i,devc->a_data_bufs[i][devc->cbuf_wrptr]);
|
||||
devc->ser_rdptr++;
|
||||
}//if channel enabled
|
||||
}//for num_a_channels
|
||||
devc->cbuf_wrptr++;
|
||||
}//While another slice available
|
||||
if(slices_avail){
|
||||
process_group(sdi,devc,slices_avail);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int send_analog(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples, uint32_t offset){
|
||||
struct sr_datafeed_packet packet;
|
||||
struct sr_datafeed_analog analog;
|
||||
struct sr_analog_encoding encoding;
|
||||
struct sr_analog_meaning meaning;
|
||||
struct sr_analog_spec spec;
|
||||
struct sr_channel *ch;
|
||||
uint32_t i;
|
||||
float *fptr;
|
||||
|
||||
sr_analog_init(&analog, &encoding, &meaning, &spec, ANALOG_DIGITS);
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
ch=devc->analog_groups[i]->channels->data;
|
||||
analog.meaning->channels = g_slist_append(NULL, ch);
|
||||
analog.num_samples = num_samples;
|
||||
analog.data = (devc->a_data_bufs[i]) + offset;
|
||||
fptr=analog.data;
|
||||
sr_spew("send analog num %d offset %d first %f 2 %f",num_samples,offset,*(devc->a_data_bufs[i]),*fptr);
|
||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
||||
analog.meaning->unit = SR_UNIT_VOLT;
|
||||
analog.meaning->mqflags = 0;
|
||||
packet.type = SR_DF_ANALOG;
|
||||
packet.payload = &analog;
|
||||
sr_session_send(sdi, &packet);
|
||||
g_slist_free(analog.meaning->channels);
|
||||
} //if enabled
|
||||
}//for channels
|
||||
return 0;
|
||||
|
||||
}
|
||||
/*Send the ring buffer of pre-trigger analog samples.
|
||||
Send the entire buffer (as long as it filled once), but need send two payloads split at the
|
||||
the writeptr */
|
||||
int send_analog_ring(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples){
|
||||
struct sr_datafeed_packet packet;
|
||||
struct sr_datafeed_analog analog;
|
||||
struct sr_analog_encoding encoding;
|
||||
struct sr_analog_meaning meaning;
|
||||
struct sr_analog_spec spec;
|
||||
struct sr_channel *ch;
|
||||
int i;
|
||||
uint32_t num_pre,start_pre;
|
||||
uint32_t num_post,start_post;
|
||||
num_pre=(num_samples>=devc->pretrig_wr_ptr) ? devc->pretrig_wr_ptr : num_samples;
|
||||
start_pre=devc->pretrig_wr_ptr-num_pre;
|
||||
num_post=num_samples-num_pre;
|
||||
start_post=devc->pretrig_entries-num_post;
|
||||
sr_dbg("send_analog ring wrptr %u ns %d npre %u spre %u npost %u spost %u",devc->pretrig_wr_ptr,num_samples,num_pre,start_pre,num_post,start_post);
|
||||
float *fptr;
|
||||
sr_analog_init(&analog, &encoding, &meaning, &spec, ANALOG_DIGITS);
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
ch=devc->analog_groups[i]->channels->data;
|
||||
analog.meaning->channels = g_slist_append(NULL, ch);
|
||||
analog.meaning->mq = SR_MQ_VOLTAGE;
|
||||
analog.meaning->unit = SR_UNIT_VOLT;
|
||||
analog.meaning->mqflags = 0;
|
||||
packet.type = SR_DF_ANALOG;
|
||||
packet.payload = &analog;
|
||||
//First send what is after the write pointer because it is oldest
|
||||
if(num_post){
|
||||
analog.num_samples = num_post;
|
||||
analog.data = (devc->a_pretrig_bufs[i]) + start_post;
|
||||
sr_dbg("ring buf %d starts at %p",i,(void *) devc->a_pretrig_bufs[i]);
|
||||
sr_dbg("analog data %d starts at %p",i,(void *) analog.data);
|
||||
sr_dbg("Sending A%d ring buffer oldest ",i);
|
||||
for(uint32_t j=0;j<analog.num_samples;j++){
|
||||
fptr=analog.data+(j*sizeof(float));
|
||||
sr_spew("RNGDCT%d j %d %f %p",i,j,*fptr,(void *)fptr);
|
||||
}
|
||||
sr_session_send(sdi, &packet);
|
||||
}
|
||||
if(num_pre){
|
||||
analog.num_samples = num_pre;
|
||||
analog.data = (devc->a_pretrig_bufs[i])+start_pre;
|
||||
sr_dbg("Sending A%d ring buffer newest ",i);
|
||||
for(uint32_t j=0;j<analog.num_samples;j++){
|
||||
fptr=analog.data+(j*sizeof(float));
|
||||
sr_spew("RNGDCW%d j %d %f %p",i,j,*fptr,(void *)fptr);
|
||||
}
|
||||
sr_session_send(sdi, &packet);
|
||||
}
|
||||
g_slist_free(analog.meaning->channels);
|
||||
sr_dbg("Sending A%d ring buffer done ",i);
|
||||
} //if enabled
|
||||
}//for channels
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//Given a chunk of slices forward to trigger check or session as appropriate and update state
|
||||
//these could be real slices or those generated by rles
|
||||
int process_group(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_slices){
|
||||
int trigger_offset;
|
||||
int pre_trigger_samples;
|
||||
//These are samples sent to session and are less than num_slices if we reach limit_samples
|
||||
size_t num_samples;
|
||||
struct sr_datafeed_logic logic;
|
||||
struct sr_datafeed_packet packet;
|
||||
int i;
|
||||
size_t cbuf_wrptr_cpy;
|
||||
cbuf_wrptr_cpy=devc->cbuf_wrptr;
|
||||
//regardless of whether we forward samples on or not, always reset the
|
||||
//pointer into the device data buffers
|
||||
devc->cbuf_wrptr=0;
|
||||
if(devc->trigger_fired){ //send directly to session
|
||||
if (devc->limit_samples &&
|
||||
num_slices > devc->limit_samples - devc->sent_samples){
|
||||
num_samples = devc->limit_samples - devc->sent_samples;
|
||||
}else{
|
||||
num_samples=num_slices;
|
||||
}
|
||||
if(num_samples>0) {
|
||||
sr_dbg("Process_group sending %d post trig samples dsb %d",num_samples,devc->dig_sample_bytes);
|
||||
if(devc->num_d_channels){
|
||||
packet.type = SR_DF_LOGIC;
|
||||
packet.payload = &logic;
|
||||
//Size the number of bytes required to fit all of the channels
|
||||
logic.unitsize = devc->dig_sample_bytes;
|
||||
//The total length of the array sent
|
||||
logic.length=num_samples*logic.unitsize;
|
||||
logic.data = devc->d_data_buf;
|
||||
sr_session_send(sdi, &packet);
|
||||
}
|
||||
send_analog(sdi,devc,num_samples,0);
|
||||
}//num_sample>0
|
||||
devc->sent_samples+=num_samples;
|
||||
return 0;
|
||||
} //trigger_fired
|
||||
else{
|
||||
size_t num_ring_samples;
|
||||
size_t sptr;
|
||||
size_t eptr;
|
||||
size_t numtail;
|
||||
size_t numwrap;
|
||||
size_t srcptr;
|
||||
sr_dbg("Process_group check %d pre trig samples",num_slices);
|
||||
//The trigger_offset is -1 if no trigger is found, but if a trigger is found
|
||||
//then trigger_offset is the offset into the data buffer sent to it.
|
||||
//The pre_trigger_samples is the total number of samples before the trigger, but limited to
|
||||
//the size of the ring buffer set by the capture_ratio. So the pre_trigger_samples can include both the new samples
|
||||
//and the ring buffer, but trigger_offset is only in relation to the new samples
|
||||
trigger_offset = soft_trigger_logic_check(devc->stl,
|
||||
devc->d_data_buf, num_slices * devc->dig_sample_bytes, &pre_trigger_samples);
|
||||
//A trigger offset >=0 indicate a trigger was seen. The stl will isue the trigger to the session
|
||||
//and will forward all pre trigger logic samples, but we must send any post trigger logic
|
||||
//and all pre and post trigger analog signals
|
||||
sr_dbg("trggr_off %d",trigger_offset);
|
||||
sr_dbg("pre_samp %d",pre_trigger_samples);
|
||||
if (trigger_offset > -1) {
|
||||
devc->trigger_fired = TRUE;
|
||||
devc->sent_samples += pre_trigger_samples;
|
||||
packet.type = SR_DF_LOGIC;
|
||||
packet.payload = &logic;
|
||||
num_samples = num_slices - trigger_offset;
|
||||
//Since we are in continuous mode for SW triggers it is possible get more samples than limit_samples, so
|
||||
//once the trigger fires make sure we don't get beyond limit samples. At this point sent_samples should
|
||||
//be equal to pre_trigger_samples (just added above) because without being triggered we'd never increment
|
||||
//sent_samples.
|
||||
//This number is the number of post trigger logic samples to send to the session, the number of floats
|
||||
//is larger.
|
||||
if (devc->limit_samples &&
|
||||
num_samples > devc->limit_samples - devc->sent_samples)
|
||||
num_samples = devc->limit_samples - devc->sent_samples;
|
||||
//The soft trigger logic issues the trigger and sends packet for all logic data that was pretrigger
|
||||
//so only send what is left
|
||||
if(num_samples>0){
|
||||
sr_dbg("Sending post trigger logical remainder of %d",num_samples);
|
||||
logic.length = num_samples * devc->dig_sample_bytes;
|
||||
logic.unitsize = devc->dig_sample_bytes;
|
||||
logic.data = devc->d_data_buf + (trigger_offset * devc->dig_sample_bytes);
|
||||
devc->sent_samples += num_samples;
|
||||
sr_session_send(sdi, &packet);
|
||||
}
|
||||
size_t new_start,new_end,new_samples,ring_samples;
|
||||
//Figure out the analog data to send.
|
||||
//We might need to send:
|
||||
//-some or all of incoming data
|
||||
//-all of incoming data and some of ring buffer
|
||||
//-all of incoming data and all of ring buffer (and still might be short)
|
||||
//We don't need to compare to limit_samples because pretrig_entries can never be more than limit_samples
|
||||
//trigger offset indicatese where in the new samples the trigger was, but we need to go back pretrig_entries before it
|
||||
new_start=(trigger_offset>(int)devc->pretrig_entries) ? trigger_offset-devc->pretrig_entries : 0;
|
||||
//Note that we might not have gotten all the pre triggerstore data we were looking for. In such a case the sw trigger
|
||||
//logic seems to fill up to the limit_samples and thus the ratio is off, but we get the full number of samples
|
||||
//The number of entries in the ring buffer is pre_trigger_samples-trigger_offset so subtract that from limit samples
|
||||
//as a threshold
|
||||
new_end=MIN(num_slices-1,devc->limit_samples-(pre_trigger_samples-trigger_offset)-1);
|
||||
//This includes pre and post trigger storage.
|
||||
new_samples=new_end-new_start+1;
|
||||
//pre_trigger_samples can never be greater than trigger_offset by more than the ring buffer depth (pretrig entries)
|
||||
ring_samples=(pre_trigger_samples>trigger_offset) ? pre_trigger_samples-trigger_offset : 0;
|
||||
sr_dbg("SW trigger float info newstart %zu new_end %zu new_samp %zu ring_samp %zu",new_start,new_end,new_samples,ring_samples);
|
||||
if(ring_samples>0){
|
||||
send_analog_ring(sdi,devc,ring_samples);
|
||||
}
|
||||
if(new_samples){
|
||||
send_analog(sdi,devc,new_samples,new_start);
|
||||
}
|
||||
|
||||
}//if trigger_offset
|
||||
else { //We didn't trigger but need to copy to ring buffer
|
||||
if((devc->a_chan_mask)&&(devc->pretrig_entries)){
|
||||
//The incoming data buffer could be much larger than the ring buffer, so never copy more than
|
||||
//the size of the ring buffer
|
||||
num_ring_samples=num_slices > devc->pretrig_entries ? devc->pretrig_entries : num_slices;
|
||||
sptr=devc->pretrig_wr_ptr; //starting pointer to copy to
|
||||
//endptr can't go past the end
|
||||
eptr=(sptr+num_ring_samples)>=devc->pretrig_entries ? devc->pretrig_entries-1 : sptr+num_ring_samples-1;
|
||||
numtail=(eptr-sptr)+1; //number of samples to copy to the tail of ring buffer without wrapping
|
||||
numwrap=(num_ring_samples>numtail) ? num_ring_samples-numtail:0;
|
||||
//cbuf_wrptr points to where the next write should go, not theactual write data
|
||||
srcptr=cbuf_wrptr_cpy-num_ring_samples;
|
||||
sr_spew("RNG num %zu sptr %zu eptr %zu ",num_ring_samples,sptr,eptr);
|
||||
sr_spew("RNG srcptr %zu nt %zu nw %zu",srcptr,numtail,numwrap);
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
//copy tail
|
||||
for(uint32_t j=0;j<numtail;j++){
|
||||
devc->a_pretrig_bufs[i][sptr+j]=devc->a_data_bufs[i][srcptr+j];
|
||||
sr_spew("RNGCpyT C%d src %zu dest %zu",i,srcptr+j,sptr+j);
|
||||
}//for j
|
||||
} //if chan_mask
|
||||
}//for channels
|
||||
//Copy wrap
|
||||
srcptr+=numtail;
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
if((devc->a_chan_mask>>i)&1){
|
||||
for(uint32_t j=0;j<numwrap;j++){
|
||||
devc->a_pretrig_bufs[i][j]=devc->a_data_bufs[i][srcptr+j];
|
||||
sr_spew("RNGCpyW C%d src %zu dest %zu",i,srcptr+j,j);
|
||||
}//for j
|
||||
}//if chan_mask
|
||||
}//for channels
|
||||
devc->pretrig_wr_ptr=(numwrap) ? numwrap : (eptr+1)%devc->pretrig_entries;
|
||||
sr_dbg("RNG pwrptr new %u",devc->pretrig_wr_ptr);
|
||||
}//if any analog channel enabled and pretrig_entries
|
||||
}//else (trigger not detected)
|
||||
}//trigger not set on function entry
|
||||
return 0;
|
||||
}//process_group
|
||||
|
||||
|
||||
//Duplicate previous sample values
|
||||
//Only duplicates up the the maximum of the devc data buffer storage and returns how many were sent
|
||||
//This assumes all previous data in the device buffers have been forwarded
|
||||
int rle_memset(struct dev_context *devc,uint32_t num_slices){
|
||||
uint32_t slices_to_send;
|
||||
uint32_t i,j,k;
|
||||
//this function assumes that all previous slices have been sent to the session
|
||||
/* todo commenting for performance
|
||||
if(devc->cbuf_wrptr){
|
||||
sr_err("ERROR:Sample buffer not previously cleared");
|
||||
return SR_ERR;
|
||||
}
|
||||
*/
|
||||
//todo - can this be removed - with a 2x ratio of sample_buf to serial and with only d4 doing rle is this check needed?
|
||||
slices_to_send=(num_slices>devc->sample_buf_size) ? devc->sample_buf_size : num_slices;
|
||||
// sr_spew("rle_memset duplicating %d of %d dig_samp %d\n",slices_to_send,num_slices,devc->dig_sample_bytes);
|
||||
//Force a known value on disabled channels, they should never be seen
|
||||
/*TODO - rle is no longer supported for any ases where analog is sent so this can likely be removed
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
for(j=0;j<slices_to_send;j++){
|
||||
devc->a_data_bufs[i][j]=((devc->a_chan_mask>>i)&1) ? devc->a_last[i] : -0.314;
|
||||
}
|
||||
}
|
||||
*/
|
||||
//Even if a channel is disabled, PV expects the same location for the enabled channels as if the channel were enabled.
|
||||
//So don't make any adjustments
|
||||
//no longer need to check for num_d_channels as rle is only on D4
|
||||
// if(devc->num_d_channels){
|
||||
for(k=0;k<devc->dig_sample_bytes;k++){
|
||||
sr_spew("rle_memset replicated group %d byte value 0x%X",k,devc->d_last[k]);
|
||||
for(j=0;j<slices_to_send;j++){
|
||||
uint32_t didx=(j*devc->dig_sample_bytes)+k;
|
||||
devc->d_data_buf[didx]=devc->d_last[k];
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
//Updating the sample write pointer probably isn't needed as well likely send the buffer to the session
|
||||
//without adding more samples at which point this will be cleared.
|
||||
devc->cbuf_wrptr=slices_to_send;
|
||||
return slices_to_send;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//This callback function is mapped from api.c with serial_source_add and is created after a capture
|
||||
//has been setup and is responsible for querying the device trigger status, downloading data
|
||||
//and forwarding packets
|
||||
SR_PRIV int sr_generic_receive(int fd, int revents, void *cb_data)
|
||||
{
|
||||
struct sr_dev_inst *sdi;
|
||||
struct dev_context *devc;
|
||||
struct sr_serial_dev_inst *serial;
|
||||
uint32_t i;
|
||||
int len;
|
||||
uint32_t bytes_rem;
|
||||
uint32_t residual_bytes;
|
||||
(void)fd;
|
||||
|
||||
if (!(sdi = cb_data))
|
||||
return TRUE;
|
||||
|
||||
if (!(devc = sdi->priv))
|
||||
return TRUE;
|
||||
sr_dbg("sr_gen_rcv");
|
||||
if(devc->rxstate!=RX_ACTIVE){
|
||||
//This condition is normal operation and expected to happen
|
||||
//but printed as informatio - todo - might get rid of it..
|
||||
sr_dbg("Reached non active state in receive %d",devc->rxstate);
|
||||
//don't return - we may be waiting for a final bytecnt
|
||||
//return TRUE;
|
||||
}
|
||||
if(devc->rxstate==RX_IDLE){
|
||||
//This is the normal end condition where we do one more receive
|
||||
sr_dbg("Reached idle state in receive %d",devc->rxstate);
|
||||
//return because we are done ? TODO-fixme - is that right?
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
serial = sdi->conn;
|
||||
//return true if it is some kind of event we don't handle
|
||||
if (!(revents == G_IO_IN || revents == 0))
|
||||
return TRUE;
|
||||
//Fill the buffer, the end may have partial slices
|
||||
bytes_rem=devc->serial_buffer_size - devc->wrptr;
|
||||
//Read one byte less so that we can null it and print as a string
|
||||
len=serial_read_blocking(serial, &(devc->buffer[devc->wrptr]), bytes_rem-1,serial_timeout(serial, 0));
|
||||
sr_dbg("Entry wrptr %u bytes_rem %u len %d",devc->wrptr,bytes_rem,len);
|
||||
|
||||
if(len>0){
|
||||
devc->buffer[devc->wrptr+len]=0;
|
||||
//Add the "#" so that spaces are clearly seen
|
||||
sr_info("rx string %s#",devc->buffer);
|
||||
//This is not guaranteed to be a dataloss condition, but definitely indicates we are
|
||||
//processing data right at the incoming rate.
|
||||
if(len>=(int)bytes_rem-8){
|
||||
sr_err("ERROR: Serial buffer near or at max depth, data from device may have been lost");
|
||||
}
|
||||
devc->bytes_avail=(devc->wrptr+len);
|
||||
//len int, bytes_avail uint32, sent_samples uint64 wrptr uint32
|
||||
sr_dbg("rx len %d bytes_avail %ul sent_samples %ul wrptr %u",len,devc->bytes_avail,devc->sent_samples,devc->wrptr);
|
||||
//sr_err("rx len %d ",len);
|
||||
}else if (len==0){
|
||||
return TRUE;
|
||||
}else {
|
||||
sr_err("ERROR:Negative serial read code %d",len);
|
||||
sdi->driver->dev_acquisition_stop(sdi);
|
||||
return FALSE;//todo/fixme -do I want false or TRUE;
|
||||
}//len>0
|
||||
//This can be used as a bit bucket to drop all samples to see how host processing time effects
|
||||
//the devices ability to send data
|
||||
// return TRUE;
|
||||
//Start reading incoming serial data at start of buffer.
|
||||
devc->ser_rdptr=0;
|
||||
if(devc->rxstate==RX_ACTIVE){
|
||||
if((devc->a_chan_mask==0)&&((devc->d_chan_mask&0xFFFFFFF0)==0)){
|
||||
process_D4(sdi,devc);
|
||||
}else{
|
||||
process_slice(sdi,devc);
|
||||
}
|
||||
}
|
||||
//process_slice/process_D4 increment ser_rdptr as bytes of the serial buffer are used
|
||||
//But they may not use all of it, and thus the residual unused bytes are shifted to the start of the buffer
|
||||
//for the next call.
|
||||
residual_bytes=devc->bytes_avail - devc->ser_rdptr;
|
||||
sr_dbg("Residuals resid %d avail %d rdptr %d wrptr %d\n",residual_bytes,devc->bytes_avail,devc->ser_rdptr,devc->wrptr);
|
||||
if(residual_bytes){
|
||||
for(i=0;i<residual_bytes;i++){
|
||||
devc->buffer[i]=devc->buffer[i+devc->ser_rdptr];
|
||||
}
|
||||
devc->ser_rdptr=0;
|
||||
devc->wrptr=residual_bytes;
|
||||
sr_dbg("Residual shift rdptr %u wrptr %u",devc->ser_rdptr,devc->wrptr);
|
||||
}else{
|
||||
//If there are no residuals shifted then zero the wrptr since all data is used
|
||||
devc->wrptr=0;
|
||||
}
|
||||
//ABORT ends immediately
|
||||
if(devc->rxstate==RX_ABORT){
|
||||
sr_err("Ending receive on abort");
|
||||
sdi->driver->dev_acquisition_stop(sdi);
|
||||
return FALSE;//todo -broken/fixme -does this work?
|
||||
}
|
||||
//if stopped look for final '+'
|
||||
if(devc->rxstate==RX_STOPPED){
|
||||
sr_dbg("Stopped, checking byte_cnt");
|
||||
if(devc->buffer[0]!='$'){
|
||||
//If this happens it means that we got a set of data that was not processed as
|
||||
//whole groups of slice bytes, so either we lost data or are not parsing it correctly.
|
||||
//TODO - treat this as a framing error?
|
||||
sr_err("ERROR: Stop marker should be byte zero");
|
||||
devc->rxstate=RX_ABORT;
|
||||
sdi->driver->dev_acquisition_stop(sdi);
|
||||
return FALSE;
|
||||
}
|
||||
for(i=1;i<devc->wrptr;i++){
|
||||
if(devc->buffer[i]=='+'){
|
||||
sr_dbg("Found plus");
|
||||
devc->buffer[i]=0;
|
||||
uint64_t rxbytecnt;
|
||||
rxbytecnt=atol(&(devc->buffer[1]));
|
||||
sr_dbg("sent cnt %llu received cnt %llu",rxbytecnt,devc->byte_cnt);
|
||||
if(rxbytecnt!=devc->byte_cnt){
|
||||
sr_err("ERROR: received %llu and counted %llu bytecnts don't match, data may be lost",rxbytecnt,devc->byte_cnt);
|
||||
}
|
||||
//We must always call acquisition_stop on all completed runs, and it sends the df_end
|
||||
sdi->driver->dev_acquisition_stop(sdi);
|
||||
std_session_send_df_end(sdi);
|
||||
devc->rxstate=RX_IDLE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
//It's possible we need one more serial transfer to get the byte_cnt,
|
||||
sr_dbg("Didn't find plus");
|
||||
}//RX_STOPPED
|
||||
//If at the sample limit, send a "+" in case we are in continuous mode and need
|
||||
//to stop the device. Not that even in non continous mode there might be cases where get an extra
|
||||
//sample or two...
|
||||
|
||||
if((devc->sent_samples>=devc->limit_samples)&&(devc->rxstate==RX_ACTIVE)){
|
||||
sr_dbg("Ending: sent %u of limit %llu samples byte_cnt %llu",
|
||||
devc->sent_samples,devc->limit_samples,devc->byte_cnt);
|
||||
send_serial_char(serial,'+');
|
||||
|
||||
}
|
||||
sr_spew("Receive function done: sent %u ",devc->sent_samples);
|
||||
sr_spew("Receive function done: wrptr %u",devc->wrptr);
|
||||
sr_spew("Receive function done: len %d",len);
|
||||
sr_spew("Receive function done: sent %u limit %llu wrptr %u len %d",devc->sent_samples,devc->limit_samples,devc->wrptr,len);
|
||||
return TRUE;
|
||||
}//sr_generic_receive
|
||||
|
||||
//Read device specific information from the device
|
||||
SR_PRIV int sr_generic_get_dev_cfg(const struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct dev_context *devc;
|
||||
struct sr_serial_dev_inst *serial;
|
||||
char *cmd, response[20];
|
||||
gchar **tokens;
|
||||
unsigned int i;
|
||||
int ret,num_tokens;
|
||||
|
||||
devc = sdi->priv;
|
||||
sr_dbg("At get_dev_cfg");
|
||||
serial = sdi->conn;
|
||||
for(i=0;i<devc->num_a_channels;i++){
|
||||
cmd = g_strdup_printf("a%d\n",i);
|
||||
ret = send_serial_w_resp(serial,cmd,response,20);
|
||||
if(ret<=0){
|
||||
sr_err("ERROR:No response from device for analog channel query");
|
||||
return SR_ERR;
|
||||
}
|
||||
tokens=NULL;
|
||||
tokens = g_strsplit(response, ",", 0);
|
||||
num_tokens = g_strv_length(tokens);
|
||||
if (num_tokens == 2) {
|
||||
devc->a_scale[i]=atof(tokens[0]);
|
||||
devc->a_offset[i]=atof(tokens[1]);
|
||||
sr_dbg("A%d scale %f offset %f\n",i,devc->a_scale[i],devc->a_offset[i]);
|
||||
}else{
|
||||
sr_err("ERROR:Ascale read c%d got unparseable response %s",i,response);
|
||||
//force a good fixed value
|
||||
devc->a_scale[i]=1/256;
|
||||
devc->a_offset[i]=0;
|
||||
}
|
||||
g_strfreev(tokens);
|
||||
g_free(cmd);
|
||||
}
|
||||
|
||||
|
||||
return SR_OK;
|
||||
|
||||
}
|
||||
|
||||
169
sigrok-generic/protocol.h
Normal file
169
sigrok-generic/protocol.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright (C) 2018 mhooijboer <marchelh@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIGROK_HARDWARE_SR_GENERIC_PROTOCOL_H
|
||||
#define LIBSIGROK_HARDWARE_SR_GENERIC_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <glib.h>
|
||||
#include <libsigrok/libsigrok.h>
|
||||
#include "libsigrok-internal.h"
|
||||
|
||||
//This is used by sr_dbg/log etc
|
||||
#define LOG_PREFIX "srgn"
|
||||
//number of bytes between markers
|
||||
#define MRK_STRIDE 128
|
||||
|
||||
//Limit to 32 since many channel enable/disable masks and other elements may be only 32 bits wide.
|
||||
#define MAX_ANALOG_CHANNELS 32
|
||||
#define MAX_DIGITAL_CHANNELS 32
|
||||
//digits input to sr_analog_init
|
||||
#define ANALOG_DIGITS 4
|
||||
//todo - remove old rle state and other defines
|
||||
#define CHAR_0 48
|
||||
#define CHAR_15 63
|
||||
|
||||
//1..16,
|
||||
//32..
|
||||
//RLEs that increment by 1
|
||||
#define CHAR_RLE_BY1_1 64
|
||||
#define CHAR_RLE_BY1_16 79
|
||||
//RLEs that increment by 16
|
||||
#define CHAR_RLE_BY16_16 80
|
||||
#define CHAR_RLE_BY16_256 95
|
||||
//RLEs that increment by 256
|
||||
#define CHAR_RLE_BY256_256 96
|
||||
#define CHAR_RLE_BY256_4096 111
|
||||
SR_PRIV int send_serial_str(struct sr_serial_dev_inst *serial, char *str);
|
||||
SR_PRIV int send_serial_char(struct sr_serial_dev_inst *serial, char ch);
|
||||
int send_serial_w_resp(struct sr_serial_dev_inst *serial, char *str,char *resp,size_t cnt);
|
||||
SR_PRIV int send_serial_w_ack(struct sr_serial_dev_inst *serial, char *str);
|
||||
|
||||
//Data Formats
|
||||
typedef enum dfmt {
|
||||
DIG1, //1 Digital channel -D0 only, packing 4 samples into one char
|
||||
DIG2, //2 Digital channels -D0 and D1 only, packing 2x2channel samples into one char
|
||||
SLICE //Slice mode - any number of digital and analog channels
|
||||
} dfmt_t;
|
||||
typedef enum rxstate {
|
||||
RX_IDLE=0,//not receiving
|
||||
RX_ACTIVE=1, //receiving data
|
||||
RX_STOPPED=2, //received stop marker, waiting for byte cnt
|
||||
RX_ABORT=3, //received aborted marker or other error
|
||||
}rxstate_t;
|
||||
struct dev_context {
|
||||
/*Configuration Parameters */
|
||||
//It is up to the user to understand sample rates and serial download speed etc and
|
||||
// do the right thing. i.e. don't select a sample rate it doesn't support, or expect
|
||||
//continuous streaming bandwidth greater than serial link speed etc...
|
||||
//The number of samples the user expects to see.
|
||||
uint64_t limit_samples;
|
||||
uint64_t sample_rate;
|
||||
//Number of samples that have been received and processed
|
||||
uint32_t num_samples;
|
||||
//Initial Number of analog and digital channels.
|
||||
//This is set by initial device config. Channels can be disabled/enabled,
|
||||
//but can not be added/removed once driver is loaded.
|
||||
uint16_t num_a_channels;
|
||||
uint16_t num_d_channels;
|
||||
//Masks of enabled channels
|
||||
uint32_t a_chan_mask;
|
||||
uint32_t d_chan_mask;
|
||||
// Channel groups -each analog channel is it's own group
|
||||
struct sr_channel_group **analog_groups;
|
||||
struct sr_channel_group *digital_group;
|
||||
//Data size in bytes for each analog channel in bytes - must be 1,2,3 or 4
|
||||
uint8_t a_size;
|
||||
//Offset and scale for each analog channel to covert 1,2,3,4bytes to float
|
||||
float a_offset[MAX_ANALOG_CHANNELS];
|
||||
float a_scale[MAX_ANALOG_CHANNELS];
|
||||
// % ratio of pre-trigger to post trigger samples
|
||||
uint64_t capture_ratio;
|
||||
// total number of bytes of data sent for one sample across all channels
|
||||
uint16_t bytes_per_slice;
|
||||
//The number of bytes needed to store all channels for one sample in the device data buff
|
||||
uint32_t dig_sample_bytes;
|
||||
/* Tracking/status once started */
|
||||
//number of bytes in the current serial input stream
|
||||
uint32_t bytes_avail;
|
||||
//Samples sent to the session */
|
||||
uint32_t sent_samples;
|
||||
//count total received bytes to detect lost info*/
|
||||
uint64_t byte_cnt;
|
||||
//For SW based triggering we put the device into continuous transmit and stop when
|
||||
// we detect a sample and capture all the samples we need. trigger_fired is thus set when
|
||||
// the sw trigger logic detects a trigger.
|
||||
//For non triggered modes we send a start and a number of samples and the device
|
||||
//transmits that much. trigger_fired is set immediately at the start.
|
||||
gboolean trigger_fired;
|
||||
//Has the device, via an "!" indicated it has stopped sending data, or has a marker
|
||||
//error been detected
|
||||
// gboolean device_stopped;
|
||||
rxstate_t rxstate;
|
||||
/* Serial Related */
|
||||
// Serial data buffer
|
||||
unsigned char *buffer;
|
||||
//Size of incoming serial buffer
|
||||
uint32_t serial_buffer_size;
|
||||
//Current byte in serial read stream that is being processed
|
||||
uint32_t ser_rdptr;
|
||||
//write pointer into the serial input buffer
|
||||
uint32_t wrptr;
|
||||
|
||||
/* Buffering Related */
|
||||
/* parsed serial read data is split into each channels dedicated buffer for analog*/
|
||||
float *a_data_bufs[MAX_ANALOG_CHANNELS];
|
||||
/*digital samples are stored packed together since cli/pulseview want it that way*/
|
||||
uint8_t *d_data_buf;
|
||||
/*write point for the the per channel data buffers*/
|
||||
uint32_t cbuf_wrptr;
|
||||
/*size of packet data buffers for each channel*/
|
||||
uint32_t sample_buf_size;
|
||||
/* RLE related*/
|
||||
/*Previous sample values to duplicate for rle */
|
||||
float a_last[MAX_ANALOG_CHANNELS];
|
||||
uint8_t d_last[MAX_DIGITAL_CHANNELS/8];
|
||||
|
||||
/* SW Trigger Related */
|
||||
struct soft_trigger_logic *stl;
|
||||
//Maximum number of entries to store pre-trigger
|
||||
uint32_t pretrig_entries;
|
||||
/* Analog pre-trigger storage for software based triggering
|
||||
because sw based only has internal storage for logic*/
|
||||
float *a_pretrig_bufs[MAX_ANALOG_CHANNELS];
|
||||
uint32_t pretrig_wr_ptr;
|
||||
|
||||
};
|
||||
|
||||
SR_PRIV int sr_generic_receive(int fd, int revents, void *cb_data);
|
||||
SR_PRIV int sr_generic_get_dev_cfg(const struct sr_dev_inst *sdi);
|
||||
|
||||
void process_D4(struct sr_dev_inst *sdi,struct dev_context *d);
|
||||
void process_slice(struct sr_dev_inst *sdi,struct dev_context *devc);
|
||||
|
||||
int send_analog(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples, uint32_t offset);
|
||||
int send_analog_ring(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples);
|
||||
|
||||
int process_group(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_slices);
|
||||
int rle_memset(struct dev_context *devc,uint32_t num_slices);
|
||||
SR_PRIV int check_marker(struct dev_context *d,int *len);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue