Add files via upload

This commit is contained in:
pico-coder 2022-02-06 21:03:01 -07:00 committed by GitHub
parent 1986908a5a
commit 1dd49b43aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 2212 additions and 0 deletions

23
pico_pgen/CMakeLists.txt Normal file
View 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
View 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

View 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
)

View 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

View 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
View 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
View 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
View 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