From: AC0BI Date: Tue, 1 Mar 2022 15:31:43 +0000 (-0700) Subject: raspberrypi-pico: First release of raspberry pi pico driver code X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=bac2a8b8f9f033d9f2c6645f1fd634c47dd0d2bb;hp=dc90146ef360d8f4e7bdf8df5b46b40e9eef3197;p=libsigrok.git raspberrypi-pico: First release of raspberry pi pico driver code --- diff --git a/src/hardware/raspberrypi-pico/api.c b/src/hardware/raspberrypi-pico/api.c index 0f5514ae..df058b9b 100644 --- a/src/hardware/raspberrypi-pico/api.c +++ b/src/hardware/raspberrypi-pico/api.c @@ -1,7 +1,7 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2022 AC0BI + * Copyright (C) 2022 Shawn Walker * * 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 @@ -16,139 +16,676 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +//debug print levels are err/warn/info/dbg/spew #include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" #include "protocol.h" -static struct sr_dev_driver raspberrypi_pico_driver_info; -static GSList *scan(struct sr_dev_driver *di, GSList *options) -{ - struct drv_context *drvc; - GSList *devices; +#define SERIALCOMM "115200/8n1" - (void)options; +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 +}; - devices = NULL; - drvc = di->context; - drvc->instances = NULL; +//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), +}; - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ +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, +}; - return devices; -} -static int dev_open(struct sr_dev_inst *sdi) -{ - (void)sdi; +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, +}; - /* TODO: get handle from sdi->conn and open it. */ +static struct sr_dev_driver raspberrypi_pico_driver_info; - return SR_OK; -} -static int dev_close(struct sr_dev_inst *sdi) +static GSList *scan(struct sr_dev_driver *di, GSList *options) { - (void)sdi; - - /* TODO: get handle from sdi->conn and close it. */ + 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,17); + 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,17); + if(num_read<10){ + sr_err("Second attempt failed"); + return NULL; + } + } + //Expected ID response is SRPICO,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,"SRPICO,A",8)) + ||(buf[11]!='D') + ||(buf[15]!='0') + ||(buf[16]!='0')){ + sr_err("ERROR:Bad response string %s %d",buf,num_read); + return NULL; + } + a_size=buf[10]-'0'; + buf[10]='\0'; //Null to end the str for atois + buf[14]='\0'; //Null to end the str for atois + num_a=atoi(&buf[8]); + num_d=atoi(&buf[12]); + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("Raspberry Pi"); + sdi->model = g_strdup("PICO"); + sdi->version = g_strdup("00"); + sdi->conn = serial; + sdi->driver = &raspberrypi_pico_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)); + 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<d_chan_mask=((1<dig_sample_bytes=((devc->num_d_channels+7)/8); + //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 group +//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 is applied +//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++){ + //Name digital channels starting at D2 to match pico board pin names + channel_name = g_strdup_printf("D%d", i+2); + 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. + //Note: The intent of making this buffer large is to prevent CDC serial buffer overflows. + //However, it is likely that if the host is running slow (i.e. it's a raspberry pi model 3) that it becomes + //compute bound and doesn't service CDC serial responses in time to not overflow the internal CDC buffers. + //And thus no serial buffer is large enough. But, it's only 256K.... + 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 in normal mode never represent more than one sample so a 2x multiplier is plenty. + //In D4 mode a serial byte can represents 100s of samples due to RLE, but process_D4 ensures that + //it breaks up the rle_memset calls to prevent overflowing the sample buffer. + //that it doesn't overflow the sample buffers. + devc->sample_buf_size=devc->serial_buffer_size*2; + for(i=0;inum_a_channels;i++){ + devc->a_data_bufs[i]=NULL; + devc->a_pretrig_bufs[i]=NULL; + } + devc->d_data_buf=NULL; + devc->sample_rate=5000; + 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; + + if(raspberrypi_pico_get_dev_cfg(sdi)!=SR_OK){ + return NULL; + }; + + 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"); + + return std_scan_complete(di, g_slist_append(NULL, sdi)); - return SR_OK; } -static int config_get(uint32_t key, GVariant **data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) -{ - int ret; - (void)sdi; - (void)data; - (void)cg; - - ret = SR_OK; - switch (key) { - /* TODO */ - default: - return SR_ERR_NA; - } - - return ret; -} +//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) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; - - (void)sdi; - (void)data; - (void)cg; - - ret = SR_OK; - switch (key) { - /* TODO */ - default: - ret = SR_ERR_NA; - } + 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; +} - 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) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; - - (void)sdi; - (void)data; - (void)cg; - - ret = SR_OK; - switch (key) { - /* TODO */ - default: - return SR_ERR_NA; - } - - return ret; + (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) { - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ - - (void)sdi; - - return SR_OK; + 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; + 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;} + + 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<index); + if(ch->enabled) { + devc->a_chan_mask|=(ch->enabled<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<index); + if(ch->enabled) { + devc->d_chan_mask|=(ch->enabled<index); + d_enabled++; + // sr_dbg("D%d en %d mask 0x%X",ch->index,ch->enabled,devc->d_chan_mask); + } + } + sr_info("Channel enable masks D 0x%X A 0x%X",devc->d_chan_mask,devc->a_chan_mask); + sprintf(tmpstr,"%c%d%d\n",ch->name[0],ch->enabled,ch->index); + if (send_serial_w_ack(serial,tmpstr) != SR_OK){ + sr_err("ERROR:Channel enable fail"); + return SR_ERR; + } else{ + + } + }//for all channels + //ensure data channels are continuous + int invalid=0; + for(i=0;i<32;i++){ + if((devc->d_chan_mask>>i)&1){ + if(invalid){ + sr_err("Digital channel mask 0x%X not continous\n\r",devc->d_chan_mask); + return SR_ERR; + } + } + else{ + invalid=1; + } + } + //recalculate bytes_per_slice. + devc->bytes_per_slice=(a_enabled*devc->a_size); + + for(i=0;inum_d_channels;i+=7){ + if(((devc->d_chan_mask)>>i)&(0x7F)){(devc->bytes_per_slice)++;} + } + if((a_enabled==0)&&(d_enabled==0)){ + sr_err("ERROR:No channels enabled"); + return SR_ERR; + } + sr_dbg("bps %d\n",devc->bytes_per_slice); + + //Apply sample rate limits + //Save off the lower rate values which are hacked way of getting configs to the device + uint8_t cfg_bits; + cfg_bits=(devc->sample_rate%10&0x6); //Only bits 2&1 are used as cfg_bits + devc->sample_rate-=cfg_bits; + sr_warn("Capture device cfg_bits of 0x%X from sample rate %lld",cfg_bits,devc->sample_rate); + if((a_enabled==3)&&(devc->sample_rate>166660)){ + sr_err("ERROR:3 channel ADC sample rate dropped to 166.660khz"); + devc->sample_rate=166660; + } + if((a_enabled==2)&&(devc->sample_rate>250000)){ + sr_err("ERROR:2 channel ADC sample rate dropped to 250khz"); + devc->sample_rate=250000; + } + if((a_enabled==1)&&(devc->sample_rate>500000)){ + sr_err("ERROR:1 channel ADC sample rate dropped to 500khz"); + devc->sample_rate=500000; + } + //Depending on channel configs, rates below 5ksps are possible + //but such a low rate can easily stream and this eliminates a lot + //of special cases. + if(devc->sample_rate<5000){ + sr_err("Sample rate override to min of 5ksps"); + devc->sample_rate=5000; + } + if(devc->sample_rate>120000000){ + sr_err("Sample rate override to max of 120Msps"); + devc->sample_rate=12000000; + } + //It may take a very large number of samples to notice, but if digital and analog are enabled + //and either PIO or ADC are fractional the samples will skew over time. + //24Mhz is the max common divisor to the 120Mhz and 48Mhz ADC clock + //so force an integer divisor to it. + if((a_enabled>0)&&(d_enabled>0)){ + if(24000000ULL%(devc->sample_rate)){ + uint32_t commondivint=24000000ULL/(devc->sample_rate); + //Always increment the divisor so that we go down in frequency to avoid max sample rate issues + commondivint++; + devc->sample_rate=24000000ULL/commondivint; + //While the common divisor is an integer, that does not mean the resulting sample rate is, and + //we want to keep the sample_rate divisible by 10 to support the cfg_bits + while((devc->sample_rate%10)&&(commondivint<4800)){ + commondivint++; + devc->sample_rate=24000000ULL/commondivint; + //sr_err(" sample rate of %llu div %u\n\r",devc->sample_rate,commondivint); + } + //Make sure the divisor increement didn't make use go too low. + if(devc->sample_rate<5000){devc->sample_rate=50000;} + sr_err("WARN: Forcing common integer divisor sample rate of %llu div %u\n\r",devc->sample_rate,commondivint); + } + + } + //If we are only digital only or only analog print a warning that the + //fractional divisors aren't a true PLL fractional feedback loop and thus + //could have sample to sample variation. + if(a_enabled>0){ + if(48000000ULL%(devc->sample_rate*a_enabled)){ + sr_warn("WARN: Non integer ADC divisor of 48Mhz clock for sample rate %llu may cause sample to sample variability.",devc->sample_rate); + } + } + if(d_enabled>0){ + if(120000000ULL%(devc->sample_rate)){ + sr_warn("WARN: Non integer PIO divisor of 120Mhz for sample rate %llu may cause sample to sample variability.",devc->sample_rate); + } + } + + + //modulo 10 to add cfg_bits back in + //All code above should create overrides that are multiples of 10, but add a check just in case. + if(devc->sample_rate%10){ + sr_err("Output sample rate %llu not mod 10",devc->sample_rate); + devc->sample_rate=(devc->sample_rate/10)*10; + } + + devc->sample_rate+=cfg_bits; + if(cfg_bits){ + sr_warn("Embedding cfg_bits of 0x%X in sample_rate %lld\n\r",cfg_bits,devc->sample_rate); + } + 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; + } + 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; + } + + + 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;inum_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;} + } + + 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;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + 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 + serial_source_add(sdi->session, serial, G_IO_IN, 200,raspberrypi_pico_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,raspberrypi_pico_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; } - +//This function is called either by the protocol code if we reached all of the samples +//or an error condition, and also by the user clicking stop in pulseview. +//It must always be called for any acquistion that was started to free memory. static int dev_acquisition_stop(struct sr_dev_inst *sdi) { - /* TODO: stop acquisition. */ - - (void)sdi; - - return SR_OK; + 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 is generally 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"); + } + if(devc->rxstate!=RX_IDLE){ + sr_err("Sending plus to stop device stream\n\r"); + send_serial_char(serial,'+'); + } + //In case we get calls to receive force it to exit + devc->rxstate=RX_IDLE; + //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;inum_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;inum_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 raspberrypi_pico_driver_info = { - .name = "raspberrypi-pico", - .longname = "RaspberryPI PICO", - .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 = dev_open, - .dev_close = dev_close, - .dev_acquisition_start = dev_acquisition_start, - .dev_acquisition_stop = dev_acquisition_stop, - .context = NULL, + .name = "raspberrypi-pico", + .longname = "RaspberryPI PICO", + .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(raspberrypi_pico_driver_info); diff --git a/src/hardware/raspberrypi-pico/protocol.c b/src/hardware/raspberrypi-pico/protocol.c index 0e954e8d..55094ee3 100644 --- a/src/hardware/raspberrypi-pico/protocol.c +++ b/src/hardware/raspberrypi-pico/protocol.c @@ -1,7 +1,7 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2022 AC0BI + * Copyright (C) 2022 Shawn Walker * * 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 @@ -17,25 +17,696 @@ * along with this program. If not, see . */ +#define _GNU_SOURCE + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" #include "protocol.h" -SR_PRIV int raspberrypi_pico_receive_data(int fd, int revents, void *cb_data) +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; + } + //100ms timeout. With USB CDC serial we can't define the timeout + //based on link rate, so just pick something large as we shouldn't normally see them + if(serial_write_blocking(serial,str,len,100) != len){ + sr_err("ERROR:Serial str write 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,100) != 1){ //100ms + sr_err("ERROR:Serial char write 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++){ //wait up to 1 second in ms increments + num_read = serial_read_blocking(serial, resp, cnt, 1); + if(num_read>0) break; + } + //sr_spew("rwprsp1 i %d nr %d",i,num_read); + //Since the serial port is usb CDC we can't calculate timeouts based on baud rate but + //even if the response is split between two USB transfers 10ms should be 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 failed (%d).", num_read); + return -1; + }else{ + 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; + //In case we have left over transfer from the device, drain them + 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 +//In this mode we can always consume all bytes because there are no cases where the processing of one +//byte requires the one after it. +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; + uint32_t sampcnt=0; //number of samples received with no rles + while(d->ser_rdptr < d->bytes_avail){ + cbyte=d->buffer[(d->ser_rdptr)]; + //RLE only byte + if(cbyte>=48 && cbyte<=127){ + rlecnt+=(cbyte-47)*8; + d->byte_cnt++; + }else if(cbyte>=0x80){ //sample with possible rle + rlecnt+=(cbyte&0x70)>>4; + if(rlecnt){ + //On a value change, duplicate the previous values first. + //The maximum value of one rle is 640. + //To ensure we don't overflow the sample buffer but still send it large chunks of data + //(to make the packet sends to the session efficient) only call process group after + //a large number of samples have been seen. + //Likely we could use the max rle value of 640 but 2048 gives some extra room. + if((rlecnt+d->cbuf_wrptr)>(d->sample_buf_size-2048)){ + //process_group is sent the number of slices which is just the cbufwrptr divided by the slice size + //This modulo check should never happen as long the calculations for dig_sample_bytes etc are + //correct, but it's a good cross check for code development. + if((d->cbuf_wrptr)%(d->dig_sample_bytes)){ + sr_err("Modulo fail %d %d ",d->cbuf_wrptr,d->dig_sample_bytes); + } + process_group(sdi,d,(d->cbuf_wrptr/d->dig_sample_bytes)); + } + rle_memset(d,rlecnt); + rlecnt=0; + sampcnt=0; + } + //Finally add in the new values + cval=cbyte&0xF; + d->d_data_buf[d->cbuf_wrptr++]=cval; + //pad in all other bytes since the sessions even wants disabled channels reported + for(j=1;jdig_sample_bytes;j++){ + d->d_data_buf[d->cbuf_wrptr++]=0; + } + sampcnt++; + d->byte_cnt++; + 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); + rlecnt=0; + + 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 + sr_spew("D4 while done rdptr %d",d->ser_rdptr); + //If we reach the end of the serial input stream send any remaining values or rles to the session + /*this can also be skipped now the rle_memset handles cbufwrptr + if(sampcnt){ + process_group(sdi,d,sampcnt); + sampcnt=0; + } + */ + if(rlecnt){ + sr_spew("Residual D4 slice rlecnt %d",rlecnt); + rle_memset(d,rlecnt); + } + if(d->cbuf_wrptr){ + sr_spew("Residual D4 data wrptr %d",d->cbuf_wrptr); + process_group(sdi,d,d->cbuf_wrptr/d->dig_sample_bytes); + + } + +}//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. +//This version handles all other enabled channel configurations that Process_D4 doesn't +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_bytesbytes_avail)&&(devc->buffer[slice_bytes-1]>=0x80);slice_bytes++); + if(slice_bytes!=devc->bytes_avail){ + cbyte=devc->buffer[slice_bytes-1]; + slice_bytes--; //Don't process the ending character + 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; + } + } + //If the wrptr is non-zero due to a residual from the previous serial transfer don't double count it towards byte_cnt + devc->byte_cnt+=slice_bytes-(devc->wrptr); + sr_spew("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 + //For digital it targets a bit, so the 3 lsbs are bit offsets within a byte + slices_avail++; + cword=0; + //build up a word 7 bits at a time, using only enabled channels + for(i=0;inum_d_channels;i+=7){ + if(((devc->d_chan_mask)>>i)&0x7F){ + cword|=((devc->buffer[devc->ser_rdptr])&0x7F)<ser_rdptr)++; + } + } + //and then distribute 8 bits at a time to all possible channels + for(i=0;inum_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 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;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + //a_size is depracted and must always be 1B + 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 t32 %d value %f wrptr %d rdptr %d sc %d off %d",i,tmp32,devc->a_data_bufs[i][devc->cbuf_wrptr],devc->cbuf_wrptr,devc->ser_rdptr,devc->a_scale[i],devc->a_offset[i]); + 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); + } + +} +//Send the processed analog values to the session +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;inum_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. +// The entire buffer is sent (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_spew("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;inum_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_spew("ring buf %d starts at %p",i,(void *) devc->a_pretrig_bufs[i]); + //sr_spew("analog data %d starts at %p",i,(void *) analog.data); + //sr_spew("Sending A%d ring buffer oldest ",i); + for(uint32_t j=0;ja_pretrig_bufs[i])+start_pre; + sr_dbg("Sending A%d ring buffer newest ",i); + for(uint32_t j=0;jchannels); + 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 (because we aren't triggered), 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_spew("Process_group sending %d post trig samples dsb %d",num_samples,devc->dig_sample_bytes); + //for(int z=0;(zd_data_buf[z]); + //} + 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_spew("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 to 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 because of the analog ring buffer we track. + 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 packest 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_spew("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;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + //copy tail + for(uint32_t j=0;ja_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;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + for(uint32_t j=0;ja_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 +//This function relies on the caller to ensure d_data_buf has samples to handle the full value of the rle +void rle_memset(struct dev_context *devc,uint32_t num_slices){ + uint32_t j,k; + sr_spew("rle_memset val 0x%X,slices %d dsb %ld\n",devc->d_last[0],num_slices,devc->dig_sample_bytes); + //Even if a channel is disabled, PV expects the same location and size for the enabled + // channels as if the channel were enabled. + for(j=0;jdig_sample_bytes;k++){ + devc->d_data_buf[devc->cbuf_wrptr++]=devc->d_last[k]; + //sr_spew("k %d j %d v 0x%X",k,j,devc->d_data_buf[(devc->cbuf_wrptr)-1]); + } + } +} + +//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 raspberrypi_pico_receive(int fd, int revents, void *cb_data) { - const struct sr_dev_inst *sdi; - struct dev_context *devc; + 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; + if(devc->rxstate!=RX_ACTIVE){ + //This condition is normal operation and expected to happen + //but printed as information + 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 + //to make sure we get the full byte_cnt + sr_dbg("Reached idle state in receive %d",devc->rxstate); + return FALSE; + } - (void)fd; + 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, note 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 + //Do a small 10ms timeout, if we get nothing, we'll always come back again + len=serial_read_blocking(serial, &(devc->buffer[devc->wrptr]), bytes_rem-1,10); + sr_spew("Entry wrptr %u bytes_rem %u len %d",devc->wrptr,bytes_rem,len); - if (!(sdi = cb_data)) - return TRUE; + if(len>0){ + devc->buffer[devc->wrptr+len]=0; + //Add the "#" so that spaces are clearly seen + sr_dbg("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. + //With the addition of the byte_cnt sent at the end we will detect any dataloss conditions + //and thus this is disabled + //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); + sr_spew("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; + }//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. Obviously no data will be forwarded to the session so it will hang + // return TRUE; - if (!(devc = sdi->priv)) - return TRUE; + //Process the serial read data + 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_spew("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;ibuffer[i]=devc->buffer[i+devc->ser_rdptr]; + } + devc->ser_rdptr=0; + devc->wrptr=residual_bytes; + sr_spew("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;// + } + //if stopped look for final '+' indicating the full byte_cnt is received + 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. + sr_err("ERROR: Stop marker should be byte zero"); + devc->rxstate=RX_ABORT; + sdi->driver->dev_acquisition_stop(sdi); + return FALSE; + } + for(i=1;iwrptr;i++){ + if(devc->buffer[i]=='+'){ + devc->buffer[i]=0; + uint64_t rxbytecnt; + rxbytecnt=atol(&(devc->buffer[1])); + sr_dbg("Byte_cnt check device cnt %llu host 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); + } + //Since we got the bytecnt we know the device is done sending data + devc->rxstate=RX_IDLE; + //We must always call acquisition_stop on all completed runs + sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + } + //It's possible we need one more serial transfer to get the byte_cnt, so print that here + sr_dbg("Haven't seen byte_cnt + yet"); + }//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 (revents == G_IO_IN) { - /* TODO */ - } + 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 limit %llu wrptr %u len %d",devc->sent_samples,devc->limit_samples,devc->wrptr,len); + return TRUE; +}//raspberrypi_pico_receive + +//Read device specific information from the device +SR_PRIV int raspberrypi_pico_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;inum_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; + } + //null end of string for strsplit + response[ret]=0; + tokens=NULL; + tokens = g_strsplit(response, "x", 0); + num_tokens = g_strv_length(tokens); + if (num_tokens == 2) { + devc->a_scale[i]=((float)atoi(tokens[0]))/1000000.0; + devc->a_offset[i]=((float)atoi(tokens[1]))/1000000.0; + sr_dbg("A%d scale %f offset %f response #%s# tokens #%s# #%s#\n",i,devc->a_scale[i],devc->a_offset[i],response,tokens[0],tokens[1]); + }else{ + sr_err("ERROR:Ascale read c%d got unparseable response %s tokens %d",i,response,num_tokens); + //force a legal fixed value assuming a 3.3V scale + //a failue in parsing the scale + devc->a_scale[i]=0.0257; + devc->a_offset[i]=0.0; + } + g_strfreev(tokens); + g_free(cmd); + } + + + return SR_OK; - return TRUE; } + diff --git a/src/hardware/raspberrypi-pico/protocol.h b/src/hardware/raspberrypi-pico/protocol.h index 4c096375..7c48fa04 100644 --- a/src/hardware/raspberrypi-pico/protocol.h +++ b/src/hardware/raspberrypi-pico/protocol.h @@ -1,7 +1,7 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2022 AC0BI + * Copyright (C) 2022 Shawn Walker * * 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 @@ -25,11 +25,128 @@ #include #include "libsigrok-internal.h" -#define LOG_PREFIX "raspberrypi-pico" +//This is used by sr_dbg/log etc +#define LOG_PREFIX "srgn" + //number of bytes between markers +#define MRK_STRIDE 128 +//This must be 32 or or less since many channel enable/disable masks and other elements may be only 32 bits wide. +//But is reduced further based on pico board limitations +#define MAX_ANALOG_CHANNELS 3 +#define MAX_DIGITAL_CHANNELS 21 +//digits input to sr_analog_init +#define ANALOG_DIGITS 4 + +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); + +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; +//TODO todo - stopped review here - renam wrptr, and review all variables 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 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 based on user input + 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 as only single byte samples are supported in this version + uint8_t a_size; + //Offset and scale for each analog channel to covert bytes 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 raspberrypi_pico_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int raspberrypi_pico_receive(int fd, int revents, void *cb_data); +SR_PRIV int raspberrypi_pico_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); +void rle_memset(struct dev_context *devc,uint32_t num_slices); +SR_PRIV int check_marker(struct dev_context *d,int *len); + + #endif