display and usb testcode, seems to work

master
Dario Ernst 15 years ago
parent 8ab62c77ce
commit edb3b0c36a

@ -0,0 +1,48 @@
# Name: Makefile
# Project: custom-class example
# Author: Christian Starkjohann
# Creation Date: 2008-04-06
# Tabsize: 4
# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
# License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
# Concigure the following definitions according to your system.
# This Makefile has been tested on Mac OS X, Linux and Windows.
# Use the following 3 lines on Unix (uncomment the framework on Mac OS X):
USBFLAGS = `libusb-config --cflags`
USBLIBS = `libusb-config --libs`
EXE_SUFFIX =
# Use the following 3 lines on Windows and comment out the 3 above. You may
# have to change the include paths to where you installed libusb-win32
#USBFLAGS = -I/usr/local/include
#USBLIBS = -L/usr/local/lib -lusb
#EXE_SUFFIX = .exe
NAME = set-led
OBJECTS = opendevice.o $(NAME).o
CC = gcc
CFLAGS = $(CPPFLAGS) $(USBFLAGS) -O -g -Wall
LIBS = $(USBLIBS)
PROGRAM = $(NAME)$(EXE_SUFFIX)
all: $(PROGRAM)
.c.o:
$(CC) $(CFLAGS) -c $<
$(PROGRAM): $(OBJECTS)
$(CC) -o $(PROGRAM) $(OBJECTS) $(LIBS)
strip: $(PROGRAM)
strip $(PROGRAM)
clean:
rm -f *.o $(PROGRAM)

@ -0,0 +1,203 @@
/* Name: opendevice.c
* Project: V-USB host-side library
* Author: Christian Starkjohann
* Creation Date: 2008-04-10
* Tabsize: 4
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.c 740 2009-04-13 18:23:31Z cs $
*/
/*
General Description:
The functions in this module can be used to find and open a device based on
libusb or libusb-win32.
*/
#include <stdio.h>
#include "opendevice.h"
/* ------------------------------------------------------------------------- */
#define MATCH_SUCCESS 1
#define MATCH_FAILED 0
#define MATCH_ABORT -1
/* private interface: match text and p, return MATCH_SUCCESS, MATCH_FAILED, or MATCH_ABORT. */
static int _shellStyleMatch(char *text, char *p)
{
int last, matched, reverse;
for(; *p; text++, p++){
if(*text == 0 && *p != '*')
return MATCH_ABORT;
switch(*p){
case '\\':
/* Literal match with following character. */
p++;
/* FALLTHROUGH */
default:
if(*text != *p)
return MATCH_FAILED;
continue;
case '?':
/* Match anything. */
continue;
case '*':
while(*++p == '*')
/* Consecutive stars act just like one. */
continue;
if(*p == 0)
/* Trailing star matches everything. */
return MATCH_SUCCESS;
while(*text)
if((matched = _shellStyleMatch(text++, p)) != MATCH_FAILED)
return matched;
return MATCH_ABORT;
case '[':
reverse = p[1] == '^';
if(reverse) /* Inverted character class. */
p++;
matched = MATCH_FAILED;
if(p[1] == ']' || p[1] == '-')
if(*++p == *text)
matched = MATCH_SUCCESS;
for(last = *p; *++p && *p != ']'; last = *p)
if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
matched = MATCH_SUCCESS;
if(matched == reverse)
return MATCH_FAILED;
continue;
}
}
return *text == 0;
}
/* public interface for shell style matching: returns 0 if fails, 1 if matches */
static int shellStyleMatch(char *text, char *pattern)
{
if(pattern == NULL) /* NULL pattern is synonymous to "*" */
return 1;
return _shellStyleMatch(text, pattern) == MATCH_SUCCESS;
}
/* ------------------------------------------------------------------------- */
int usbGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
{
char buffer[256];
int rval, i;
if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
return rval;
if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
return rval;
if(buffer[1] != USB_DT_STRING){
*buf = 0;
return 0;
}
if((unsigned char)buffer[0] < rval)
rval = (unsigned char)buffer[0];
rval /= 2;
/* lossy conversion to ISO Latin1: */
for(i=1;i<rval;i++){
if(i > buflen) /* destination buffer overflow */
break;
buf[i-1] = buffer[2 * i];
if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
buf[i-1] = '?';
}
buf[i-1] = 0;
return i-1;
}
/* ------------------------------------------------------------------------- */
int usbOpenDevice(usb_dev_handle **device, int vendorID, char *vendorNamePattern, int productID, char *productNamePattern, char *serialNamePattern, FILE *printMatchingDevicesFp, FILE *warningsFp)
{
struct usb_bus *bus;
struct usb_device *dev;
usb_dev_handle *handle = NULL;
int errorCode = USBOPEN_ERR_NOTFOUND;
usb_find_busses();
usb_find_devices();
for(bus = usb_get_busses(); bus; bus = bus->next){
for(dev = bus->devices; dev; dev = dev->next){ /* iterate over all devices on all busses */
if((vendorID == 0 || dev->descriptor.idVendor == vendorID)
&& (productID == 0 || dev->descriptor.idProduct == productID)){
char vendor[256], product[256], serial[256];
int len;
handle = usb_open(dev); /* we need to open the device in order to query strings */
if(!handle){
errorCode = USBOPEN_ERR_ACCESS;
if(warningsFp != NULL)
fprintf(warningsFp, "Warning: cannot open VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
continue;
}
/* now check whether the names match: */
len = vendor[0] = 0;
if(dev->descriptor.iManufacturer > 0){
len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, vendor, sizeof(vendor));
}
if(len < 0){
errorCode = USBOPEN_ERR_ACCESS;
if(warningsFp != NULL)
fprintf(warningsFp, "Warning: cannot query manufacturer for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
}else{
errorCode = USBOPEN_ERR_NOTFOUND;
/* printf("seen device from vendor ->%s<-\n", vendor); */
if(shellStyleMatch(vendor, vendorNamePattern)){
len = product[0] = 0;
if(dev->descriptor.iProduct > 0){
len = usbGetStringAscii(handle, dev->descriptor.iProduct, product, sizeof(product));
}
if(len < 0){
errorCode = USBOPEN_ERR_ACCESS;
if(warningsFp != NULL)
fprintf(warningsFp, "Warning: cannot query product for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
}else{
errorCode = USBOPEN_ERR_NOTFOUND;
/* printf("seen product ->%s<-\n", product); */
if(shellStyleMatch(product, productNamePattern)){
len = serial[0] = 0;
if(dev->descriptor.iSerialNumber > 0){
len = usbGetStringAscii(handle, dev->descriptor.iSerialNumber, serial, sizeof(serial));
}
if(len < 0){
errorCode = USBOPEN_ERR_ACCESS;
if(warningsFp != NULL)
fprintf(warningsFp, "Warning: cannot query serial for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
}
if(shellStyleMatch(serial, serialNamePattern)){
if(printMatchingDevicesFp != NULL){
if(serial[0] == 0){
fprintf(printMatchingDevicesFp, "VID=0x%04x PID=0x%04x vendor=\"%s\" product=\"%s\"\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product);
}else{
fprintf(printMatchingDevicesFp, "VID=0x%04x PID=0x%04x vendor=\"%s\" product=\"%s\" serial=\"%s\"\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product, serial);
}
}else{
break;
}
}
}
}
}
}
usb_close(handle);
handle = NULL;
}
}
if(handle) /* we have found a deice */
break;
}
if(handle != NULL){
errorCode = 0;
*device = handle;
}
if(printMatchingDevicesFp != NULL) /* never return an error for listing only */
errorCode = 0;
return errorCode;
}
/* ------------------------------------------------------------------------- */

@ -0,0 +1,77 @@
/* Name: opendevice.h
* Project: V-USB host-side library
* Author: Christian Starkjohann
* Creation Date: 2008-04-10
* Tabsize: 4
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.h 755 2009-08-03 17:01:21Z cs $
*/
/*
General Description:
This module offers additional functionality for host side drivers based on
libusb or libusb-win32. It includes a function to find and open a device
based on numeric IDs and textual description. It also includes a function to
obtain textual descriptions from a device.
To use this functionality, simply copy opendevice.c and opendevice.h into your
project and add them to your Makefile. You may modify and redistribute these
files according to the GNU General Public License (GPL) version 2 or 3.
*/
#ifndef __OPENDEVICE_H_INCLUDED__
#define __OPENDEVICE_H_INCLUDED__
#include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */
#include <stdio.h>
int usbGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen);
/* This function gets a string descriptor from the device. 'index' is the
* string descriptor index. The string is returned in ISO Latin 1 encoding in
* 'buf' and it is terminated with a 0-character. The buffer size must be
* passed in 'buflen' to prevent buffer overflows. A libusb device handle
* must be given in 'dev'.
* Returns: The length of the string (excluding the terminating 0) or
* a negative number in case of an error. If there was an error, use
* usb_strerror() to obtain the error message.
*/
int usbOpenDevice(usb_dev_handle **device, int vendorID, char *vendorNamePattern, int productID, char *productNamePattern, char *serialNamePattern, FILE *printMatchingDevicesFp, FILE *warningsFp);
/* This function iterates over all devices on all USB busses and searches for
* a device. Matching is done first by means of Vendor- and Product-ID (passed
* in 'vendorID' and 'productID'. An ID of 0 matches any numeric ID (wildcard).
* When a device matches by its IDs, matching by names is performed. Name
* matching can be done on textual vendor name ('vendorNamePattern'), product
* name ('productNamePattern') and serial number ('serialNamePattern'). A
* device matches only if all non-null pattern match. If you don't care about
* a string, pass NULL for the pattern. Patterns are Unix shell style pattern:
* '*' stands for 0 or more characters, '?' for one single character, a list
* of characters in square brackets for a single character from the list
* (dashes are allowed to specify a range) and if the lis of characters begins
* with a caret ('^'), it matches one character which is NOT in the list.
* Other parameters to the function: If 'warningsFp' is not NULL, warning
* messages are printed to this file descriptor with fprintf(). If
* 'printMatchingDevicesFp' is not NULL, no device is opened but matching
* devices are printed to the given file descriptor with fprintf().
* If a device is opened, the resulting USB handle is stored in '*device'. A
* pointer to a "usb_dev_handle *" type variable must be passed here.
* Returns: 0 on success, an error code (see defines below) on failure.
*/
/* usbOpenDevice() error codes: */
#define USBOPEN_SUCCESS 0 /* no error */
#define USBOPEN_ERR_ACCESS 1 /* not enough permissions to open device */
#define USBOPEN_ERR_IO 2 /* I/O error */
#define USBOPEN_ERR_NOTFOUND 3 /* device not found */
/* Obdev's free USB IDs, see USB-IDs-for-free.txt for details */
#define USB_VID_OBDEV_SHARED 5824 /* obdev's shared vendor ID */
#define USB_PID_OBDEV_SHARED_CUSTOM 1500 /* shared PID for custom class devices */
#define USB_PID_OBDEV_SHARED_HID 1503 /* shared PID for HIDs except mice & keyboards */
#define USB_PID_OBDEV_SHARED_CDCACM 1505 /* shared PID for CDC Modem devices */
#define USB_PID_OBDEV_SHARED_MIDI 1508 /* shared PID for MIDI class devices */
#endif /* __OPENDEVICE_H_INCLUDED__ */

Binary file not shown.

@ -0,0 +1,82 @@
/* Name: set-led.c
* Project: custom-class, a basic USB example
* Author: Christian Starkjohann
* Creation Date: 2008-04-10
* Tabsize: 4
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: set-led.c 692 2008-11-07 15:07:40Z cs $
*/
/*
General Description:
This is the host-side driver for the custom-class example device. It searches
the USB for the LEDControl device and sends the requests understood by this
device.
This program must be linked with libusb on Unix and libusb-win32 on Windows.
See http://libusb.sourceforge.net/ or http://libusb-win32.sourceforge.net/
respectively.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <usb.h> /* this is libusb */
#include "opendevice.h" /* common code moved to separate module */
#include "../firmware/usbdrv/usbconfig.h" /* device's VID/PID and names */
int main(int argc, char **argv)
{
usb_dev_handle *handle = NULL;
const unsigned char rawVid[2] = {USB_CFG_VENDOR_ID}, rawPid[2] = {USB_CFG_DEVICE_ID};
char vendor[] = {USB_CFG_VENDOR_NAME, 0}, product[] = {USB_CFG_DEVICE_NAME, 0};
char buffer[4];
int cnt, vid, pid, isOn;
usb_init();
/* compute VID/PID from usbconfig.h so that there is a central source of information */
vid = rawVid[1] * 256 + rawVid[0];
pid = rawPid[1] * 256 + rawPid[0];
/* The following function is in opendevice.c: */
if(usbOpenDevice(&handle, vid, vendor, pid, product, NULL, NULL, NULL) != 0){
fprintf(stderr, "Could not find USB device \"%s\" with vid=0x%x pid=0x%x\n", product, vid, pid);
exit(1);
}
/* Since we use only control endpoint 0, we don't need to choose a
* configuration and interface. Reading device descriptor and setting a
* configuration and interface is done through endpoint 0 after all.
* However, newer versions of Linux require that we claim an interface
* even for endpoint 0. Enable the following code if your operating system
* needs it: */
#if 0
int retries = 1, usbConfiguration = 1, usbInterface = 0;
if(usb_set_configuration(handle, usbConfiguration) && showWarnings){
fprintf(stderr, "Warning: could not set configuration: %s\n", usb_strerror());
}
/* now try to claim the interface and detach the kernel HID driver on
* Linux and other operating systems which support the call. */
while((len = usb_claim_interface(handle, usbInterface)) != 0 && retries-- > 0){
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
if(usb_detach_kernel_driver_np(handle, 0) < 0 && showWarnings){
fprintf(stderr, "Warning: could not detach kernel driver: %s\n", usb_strerror());
}
#endif
}
#endif
int rxValue, rxIndex;
int value = 0, index = 0;
int i=0;
cnt = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 2, value, index, buffer, sizeof(buffer), 5000);
if(cnt < 0){
fprintf(stderr, "\nUSB error in iteration %d: %s\n", i, usb_strerror());
}
rxValue = ((int)buffer[1] & 0xff) | (((int)buffer[0] & 0xff) << 8);
rxIndex = ((int)buffer[3] & 0xff) | (((int)buffer[2] & 0xff) << 8);
fprintf(stderr, "rxValue = %3d.%02d \n", rxValue/100, rxValue%100);
fprintf(stderr, "rxIndex = %3d.%02d \n", rxIndex/100, rxIndex%100);
usb_close(handle);
return 0;
}

Binary file not shown.

@ -1,7 +1,7 @@
DEFINES += -DF_CPU=16000000
CFLAGS += -save-temps
OBJECTS = usbdrv/usbdrvasm.o usbdrv/usbdrv.o main.o display.o lcd/lcd.o spi.o
OBJECTS = usbdrv/usbdrvasm.o usbdrv/usbdrv.o main.o display.o lcd/lcd.o

@ -3,98 +3,119 @@
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <stdio.h>
#include <usbdrv/usbdrv.h>
#include "main.h"
#include "display.h"
#include "spi.h"
void hardinit(){
/* initializes the hardware */
DDRB = _BV(1) | _BV(2) | _BV(0);
DDRC = 0x3f;
static uint8_t newThermoData = 1;
static uint16_t thermoData[] = {1024, 814, 2475, 2243};
// no pullups; all digital outputs can take care of themselves
PORTB = 0x00;
PORTC = 0x00;
PORTD = 0x00;
/* hardware pwm for brightness/contrast:
*/
void hardinit() {
/* initializes the hardware */
DDRB = _BV(1) | _BV(2) | _BV(0);
DDRC = 0x3f;
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
TCCR1B = _BV(WGM12) | _BV(CS11); // clk/8 prescaler, 4kHz PWM-freq.
// no pullups; all digital outputs can take care of themselves
PORTB = 0x00;
PORTC = 0x00;
PORTD = 0x00;
OCR1A = 15; // contrast
OCR1B = 50; // brightness
// init LCD:
lcd_init(1);
lcd_clrscr(1);
// hardware pwm for brightness/contrast:
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
TCCR1B = _BV(WGM12) | _BV(CS11); // clk/8 prescaler, 4kHz PWM-freq.
// 1wire needs no own init, bus search is done in softinit
OCR1A = 15; // contrast
OCR1B = 50; // brightness
// init LCD:
lcd_init(1);
lcd_clrscr(1);
sei();
sei();
}
void softinit(){
lcd_defchar(1, LCD_CHAR_HALFBAR, lcd_halfbar_char);
lcd_defchar(1, LCD_CHAR_BAR, lcd_bar_char);
lcd_defchar(1, LCD_CHAR_DEGREE, lcd_degree_char);
void softinit() {
lcd_defchar(1, LCD_CHAR_HALFBAR, lcd_halfbar_char);
lcd_defchar(1, LCD_CHAR_BAR, lcd_bar_char);
lcd_defchar(1, LCD_CHAR_DEGREE, lcd_degree_char);
}
usbMsgLen_t usbFunctionSetup(uchar data[8]) {
usbRequest_t *rq = (void *)data;
static uchar dataBuffer[4]; /* buffer must stay valid when usbFunctionSetup returns */
if(rq->bRequest == 1){ /* echo -- used for reliability tests */
dataBuffer[0] = (thermoData [0] & 0xff00)>>8;
dataBuffer[1] = thermoData [0] & 0x00ff;
dataBuffer[2] = (thermoData [1] & 0xff00)>>8;
dataBuffer[3] = thermoData [1] & 0x00ff;
usbMsgPtr = dataBuffer; /* tell the driver which data to return */
return 4;
}
if(rq->bRequest == 2){ /* echo -- used for reliability tests */
dataBuffer[0] = (thermoData [2] & 0xff00)>>8;
dataBuffer[1] = thermoData [2] & 0x00ff;
dataBuffer[2] = (thermoData [3] & 0xff00)>>8;
dataBuffer[3] = thermoData [3] & 0x00ff;
usbMsgPtr = dataBuffer; /* tell the driver which data to return */
return 4;
}
return 0; /* default for not implemented requests: return no data back to host */
}
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
static uchar dataBuffer[4]; /* buffer must stay valid when usbFunctionSetup returns */
if(rq->bRequest == 1){ /* echo -- used for reliability tests */
dataBuffer[0] = rq->wValue.bytes[0];
dataBuffer[1] = rq->wValue.bytes[1];
dataBuffer[2] = rq->wIndex.bytes[0];
dataBuffer[3] = rq->wIndex.bytes[1];
usbMsgPtr = dataBuffer; /* tell the driver which data to return */
return 4;
}
return 0; /* default for not implemented requests: return no data back to host */
void updateDisplay() {
if(newThermoData==1) {
newThermoData = 0;
char str[34];
snprintf(str, 34, "%3d.%02d\x03||%3d.%02d\x03\n%3d.%02d\x03||%3d.%02d\x03", thermoData[0]/100, thermoData[0]%100,
thermoData[1]/100, thermoData[1]%100,
thermoData[2]/100, thermoData[2]%100,
thermoData[3]/100, thermoData[3]%100
);
display_gotoyx(0,0);
display_puts(str);
display_update();
}
}
int __attribute__((noreturn)) main(void) {
int i=0;
hardinit();
softinit();
usbInit();
int __attribute__((noreturn)) main(void){
hardinit();
softinit();
usbInit();
display_puts("Hallo, Welt!\n\n");
display_update();
display_puts("Hallo, Welt!\n\n");
display_update();
for(;;){
usbPoll();
for(;;){
usbPoll();
}
updateDisplay();
}
thermoData[0]=thermoData[0]+5;
thermoData[1]=thermoData[1]+15;
thermoData[2]=thermoData[2]+7;
thermoData[3]=thermoData[3]+18;
i++; if(i%200 == 0) newThermoData = 1;
}
}
/*
ISR(TIMER1_OVF_vect, ISR_NAKED){
asm volatile ("in %0, %1\n" : "=r" (sreg_store) : "I" (_SFR_IO_ADDR(SREG)));
timer1_acc++;
asm volatile ("out %1, %0\n" : "=r" (sreg_store) : "I" (_SFR_IO_ADDR(SREG)));
reti();
}*/
ISR(TIMER1_OVF_vect, ISR_NAKED){
asm volatile ("in %0, %1\n" : "=r" (sreg_store) : "I" (_SFR_IO_ADDR(SREG)));
timer1_acc++;
asm volatile ("out %1, %0\n" : "=r" (sreg_store) : "I" (_SFR_IO_ADDR(SREG)));
reti();
}*/

@ -16,6 +16,7 @@
#ifndef __MAIN_H
#define __MAIN_H
#include <stdint.h>
#define LED1PORT PORTC

@ -34,23 +34,20 @@ void spi_init(){
d = spi_clock_divisor();
if(d>7){
#warning "spi baudrate too slow, cannot be set"
#warning "spi baudrate too slow, cannot be set"
d=7;
}
//TODO: DDRs setzen
spsr = 0;
spsr |= (d & 1) ? 0 : _BV(SPI2X);
spcr = 0 | _BV(SPIE) | _BV(SPE);
spcr |= (d & 2) ? _BV(SPR0) : 0;
spcr |= (d & 4) ? _BV(SPR1) : 0;
#ifdef SPI_MASTER
spcr |= _BV(MSTR);
#endif
SPCR = spcr;
SPSR = spsr;
@ -60,7 +57,6 @@ void spi_init(){
spi_writeptr = NULL;
}
// return true on success, false on error
uint8_t spi_write(uint8_t *data, uint8_t len){
if(spi_writeptr != NULL){
@ -72,8 +68,6 @@ uint8_t spi_write(uint8_t *data, uint8_t len){
return 1;
}
#ifdef SPI_MASTER
ISR(SPI_vector){

@ -1,6 +1,6 @@
#define SPI_BAUDRATE 1000000
#define SPI_MASTER 1
#define SPI_READBUF_LEN 32
uint8_t spi_write(uint8_t *data, uint8_t len);
void spi_init();

Loading…
Cancel
Save