So I've been using commandblock's serial library to send screenshots from my desktop to the TI-84+ CE. I want to eventually get the screenshots at 30 fps so that the display of the calculator basically mirrors my computer. I can get smallish screenshots displayed on the calculator at like 3-5 fps, but if I try increasing how fast I send data at the calculator, the calculator doesn't display all the images any smoother. When I increase the size of the image, the calculator also seems to skip a lot more images and slows down how fast it shows the images. Right now I'm using xlib to make a screenshot and Mateos' convimg to convert to a sprite. Then I send the bytes via serial to the calculator.

Is it even possible to increase the "frame rate"?

Code:

Code:

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tice.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <keypadc.h>

#define usb_callback_data_t usb_device_t

#include <usbdrvce.h>
#include <srldrvce.h>
#include <graphx.h>


/* Include the converted graphics file */
#include "gfx/gfx.h"

uint8_t srl_buf[4000];

/* Handle USB events */
static usb_error_t handle_usb_event(usb_event_t event, void *event_data,
                                  usb_callback_data_t *callback_data) {
    /* When a device is connected, or when connected to a computer */
    if(event == USB_DEVICE_CONNECTED_EVENT || event == USB_HOST_CONFIGURE_EVENT) {
        if(!*callback_data) {
            /* Set the USB device */
            *callback_data = event_data;
        }
    }

    /* When a device is disconnected */
    if(event == USB_DEVICE_DISCONNECTED_EVENT) {
        *callback_data = NULL;
    }

    return USB_SUCCESS;
}

gfx_UninitedSprite(sprite_buffer, oiram_width, oiram_height);

void main(void)
{

    usb_error_t usb_error;
    srl_error_t srl_error;

    usb_device_t usb_device = NULL;
    srl_device_t srl;

    /* A buffer for internal use by the serial library */

    /* Initialize the USB driver with our event handler and the serial device descriptors */
    usb_error = usb_Init(handle_usb_event, &usb_device, srl_GetCDCStandardDescriptors(), USB_DEFAULT_INIT_FLAGS);
    if(usb_error) goto exit;
   
    /* Wait for a USB device to be connected */
    while(!usb_device) {
        kb_Scan();

        /* Exit if clear is pressed */
        if(kb_IsDown(kb_KeyClear)) {
            goto exit;
        }
        /* Handle any USB events that have occured */
        usb_HandleEvents();
    }

    /* Initialize the serial library with the USB device */
    srl_error = srl_Init(&srl, usb_device, srl_buf, sizeof(srl_buf), SRL_INTERFACE_ANY);
    if(srl_error) goto exit;

    /* Clear the homescreen */
    os_ClrHome();

    /* Print a string */
    os_PutStrFull("Initialized USB Successfully!");



    /* Initialize graphics drawing */
    gfx_Begin();

    /* Set the palette for sprites */
    gfx_SetPalette(xlibc, sizeof_xlibc, 0);

    /* These were set in the image conversion file */
    gfx_SetTransparentColor(0);
    gfx_FillScreen(255);

    do {
        /* A buffer to store bytes read by the serial library */
        int bytes_read = 0;
       
        kb_Scan();
        usb_HandleEvents();

        /* If the device was disconnected, exit */
        if(!usb_device) break;


        bytes_read = srl_Read_Blocking(&srl, oiram_data, sizeof(oiram_data), 600);

        /* Only write when bytes were received */
        if(bytes_read == sizeof(oiram_data)) {

            gfx_Sprite_NoClip(gfx_FlipSpriteX(oiram, sprite_buffer), 60, 60);
        }




    } while(!kb_IsDown(kb_KeyClear));




    /* A transparent sprite allows the background to show */
    //gfx_TransparentSprite_NoClip(oiram, 190, 110);

    /* End graphics drawing */
    gfx_End();

    exit:
    usb_Cleanup();
}


Demo:


Things that I've tried:
▪ Increasing srl_buf size
▪ Setting the baud rate higher
▪ Using the nonblocking version of srl_read(). I couldn't get the regular srl_read() to receive all the data and display images properly. Would getting this to work improve speed?

Commandblockguy said that "it's probably the code copying to the screen that's slowing it down." How would I address that issue if this were the case.

Commandblockguy also suggested using the raw usb library at the usbdrvce branch instead of the serial library to send data; however, I'm having some trouble getting it to send large amounts of data and some question about it.

What kind of transfers/endpoints does the library support? usbdrvce.h has a typedef making bulk, interrupt, and Isochronous transfers usb_ScheduleTransfer. Does this mean that all 3 types use the same function?

When I used pyUSB on my computer, I only saw 2 endpoints, both of which said BULK:

Code:

ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0

Does this mean that there are only 2 endpoints available?

Also when I try sending more than 1024 bytes to the calculator, pyUSB only sends 1024 bytes.
Since the above says that the Max Packet Size is 64, I added the following on line 233:

Code:

             int length = 1024 + 64;
             if (!(buffer = malloc(length))) {                                   
                  error = USB_ERROR_NO_MEMORY;                                   
                  break;                                                         
              }                                                                   
                                                                                 
              for (i = 0; i < (int)ceil(length/64.0); i++) {                     
                  usb_ScheduleBulkTransfer(usb_GetDeviceEndpoint((usb_device_t)event_data, 0x02), buffer + i*64, 64, handleBulkOut, buffer + i*64);
              }                                                                   
              free(buffer);                                                       
              break;


When I send 1024 + 64 bytes using pyUSB, pyUSB quits writing after 1024 bytes.
Is this a problem with the computer or with the calculator?

The python code:

Code:

#!/usr/bin/python3

import sys
import usb.core

VENDOR_ID = 0x451
PRODUCT_ID = 0xe008

INTERFACE = 0
SETTING = 0
ENDPOINT = 1

device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
if device is None:
    raise ValueError('Device not found')

# By default, the kernel will claim the device and make it available via
# /dev/usb/hiddevN and /dev/hidrawN which also prevents us
# from communicating otherwise. This removes these kernel devices.
# Yes, it is weird to specify an interface before we get to a configuration.
if device.is_kernel_driver_active(INTERFACE):
    print("Detaching kernel driver")
    device.detach_kernel_driver(INTERFACE)

# set the active configuration. With no arguments, the first
# configuration will be the active one
device.set_configuration()
   
configuration = device.get_active_configuration()

interface = configuration[(INTERFACE, SETTING)]

print(configuration)

endpoint = interface[ENDPOINT]

# endpoint = usb.util.find_descriptor(
#     interface,
#     # match the first OUT endpoint
#     custom_match = \
#     lambda e: \
#         usb.util.endpoint_direction(e.bEndpointAddress) == \
#         usb.util.ENDPOINT_OUT)

assert endpoint is not None

print("Writing data")
print(endpoint.write("A"*(1024+64)))


Thanks for reading all this.
Your speed issue might be caused partially by gfx_FlipSpriteX. I'd try removing that and seeing if it helps. Also, using the non-blocking function would allow you to draw a frame to the screen while getting the next frame over USB at the same time.

Quote:
Commandblockguy said that "it's probably the code copying to the screen that's slowing it down." How would I address that issue if this were the case.

You can't really do much about this using srldrvce. If you're using usbdrvce, you can tell it write data directly to the screen if the image is 320 pixels wide.

About endpoints: when the calc is acting as a host, only control and bulk endpoints work. However, in device mode (which is what you're using), I believe that interrupt transfers and possible isochronous also work.

The endpoints available on a USB device are determined by a device's USB descriptors. usbdrvce allows you to set your own custom USB descriptors, and the descriptors you are seeing with pyUSB are set by srldrvce, and not inherent to the calculator. The number of endpoints isn't particularly relevant for your case, as endpoints are used to split up communications by transfer type, direction, and by logical function. You'll really only need one bulk (or isochronous, if that works) out endpoint for your program.

About the 1024 byte limit: that's part of the USB specification. You should use multiple transfers of 1024 bytes each.

About the maximum packet size: usbdrvce allows you to completely ignore that. If you try to send or receive more than 64 bytes, it will automatically be split up into multiple packets with a max size of 64 bytes, and recombined into the full transfer data once all packets have been received. So, just try to receive as many bytes as you need, except when that would exceed the 1024 byte limit.
Quote:
Your speed issue might be caused partially by gfx_FlipSpriteX. I'd try removing that and seeing if it helps.

I removed gfx_FlipSpriteX and just used gfx_Sprite_NoClip(); however, this doesn't seem to improve the speed.

Quote:
Also, using the non-blocking function would allow you to draw a frame to the screen while getting the next frame over USB at the same time.

I tried replacing

Code:
bytes_read = srl_Read_Blocking(&srl, oiram_data, sizeof(oiram_data), 600);
with
Code:
bytes_read = srl_Read(&srl, oiram_data, sizeof(oiram_data));
, but this makes bytes_read never equal to oiram_data, so it looks not all the data is being sent over.
srl_Read only copies the data that's already available. You could either srl_Read repeatedly until you receive all the bytes, or call usb_HandleEvents and srl_Available repeatedly until srl_Available returns a value greater than the size of the sprite, then srl_Read.
What is wrong with this implementation of using srl_Read repeatedly?

Code:

int bytes_read = 0;
do {                                                                       
          kb_Scan();                                                             
          usb_HandleEvents();                                                     
                                                                                 
          /* If the device was disconnected, exit */                             
          if(!usb_device) break;                                                 
                                                                                 
                                                                                 
          /* Read up to 64 bytes from serial */                                     
          bytes_read += srl_Read(&srl, oiram_data + bytes_read, sizeof(oiram_data));
                                                                                 
          /* Only write when bytes were received */                               
          if(bytes_read == sizeof(oiram_data)) {                                 
              /* Write the same bytes back to the other device */                 
              //srl_Write(&srl, in_buf, bytes_read);                             
                                                                                 
              /* Clear the homescreen */                                         
                  //os_ClrHome();                                                 
                                                                                 
                  //itoa(bytes_avail, buf);                                       
                                                                                 
              /* Print a string */                                               
                  //os_PutStrFull(buf);                                           
                                                                                 
              //memcpy(oiram_data, in_buf, sizeof(in_buf));                       
              gfx_Sprite(oiram, 60, 60);                                         
              bytes_read = 0;                                                     
          }                                                                       
                                                                                 
                                                                                 
                                                                                 
                                                                                 
      } while(!kb_IsDown(kb_KeyClear));                     
I'm able to send around 14 kB at a time to the calc, but after about 15 seconds, the calculator freezes up and sometimes resets. I'm using
Code:
watch -n0.2 python3 test.py
to send 14kB at a time to the calculator every 0.2 seconds.

Python code (Computer side):

Code:

""" PyUSB setup stuff """
""" ... """

buf = "A"*13952

size = len(buf)

for i in range(0, size, 1024):
    if (i >= size - 1024):
        endpoint.write(buf[i:])
    else:
        endpoint.write(buf[i:i+1024])



Calculator side: (Just used the script here and replace 512 with 13952.

Code:

if (!(buffer = malloc(13952))) {
                error = USB_ERROR_NO_MEMORY;
                break;
            }
            usb_ScheduleBulkTransfer(usb_GetDeviceEndpoint((usb_device_t)event_data, 0x02), buffer, 13952, handleBulkOut, buffer);


Is the calculator not able to handle the amount of data being transferred because I'm using the default USB descriptors and a bulk transfer?
Try sending zero bytes (0) as opposed to all one bytes (255).
Is using the USB_HOST_CONFIGURE_EVENT case even the proper way to handle data transfers or is there a more efficient way of receiving data without configuring every time I send data? I tried receiving 0xFF and 0x00 2048 bytes, 4096 bytes, and 14kB at a time, but the calculator still either reset itself or froze.
You should schedule your first data transfer in USB_HOST_CONFIGURE_EVENT, then schedule a new transfer in the transfer callback. USB devices are generally only supposed to be configured rarely, so doing so repeatedly might cause weird bugs.
Thanks! I can now get data transferred using usbdrvce now.

A few questions on how to draw the data I'm receiving via USB:
I'm using gfx_Sprite() to display the data and it works fine when the sprite is less than 150px in width.

But when the width of the sprite gets larger, the screen starts to flicker. I tried using both the methods of buffering as described here, but the following is the result when using buffering:


Here's the code I'm using for buffering:



Code:

static usb_error_t handleBulkOut(usb_endpoint_t endpoint,                       
                                   usb_transfer_status_t status,                 
                                   size_t transferred, usb_transfer_data_t *data) {
                                                                       
      gfx_Sprite(oiram, 60, 60);                                                 
      gfx_SwapDraw();                                                             
                                                                                 
      //free(data);                                                               
      usb_ScheduleBulkTransfer(endpoint, data, BUF_SIZE, handleBulkOut, data);       
      return USB_SUCCESS;                                                         
  }

void main(void)
{

      // USB Stuff
      .........................................

      /* Initialize graphics drawing */                                           
      gfx_Begin();                                                               
                                                                                 
      /* Set the palette for sprites */                                           
      gfx_SetPalette(xlibc, sizeof_xlibc, 0);                                     
                                                                                 
      /* These were set in the image conversion file */                           
      gfx_SetTransparentColor(0);                                                 
      gfx_FillScreen(255);                                                       
      gfx_SetDrawBuffer();     
}


Also instead of using sprites, is it possible to write the data to the LCD via DMA using the toolchain?

EDIT: I used commandz advice to write directly to the gfx_vram, but I'm still getting flickering. The top half of the screen looks fine, but the bottom half visibly refreshes. Is this an issue with the usb transfer or is there another reason why only the bottom half of the screen would be flickering?

Code:

usb_ScheduleBulkTransfer(usb_GetDeviceEndpoint((usb_device_t)event_data, 0x02), gfx_vram, BUF_SIZE, handleBulkOut, gfx_vram);

  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are GMT - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement