I'm developing a C app using libusb-1.0. I want to get some config parameters related to usb devices. My question is related to bcdUSB parameter. My code is like following:
libusb_device *dev;
struct libusb_device_descriptor desc;
....
ret = libusb_get_device_descriptor(dev, &desc);
if (ret<0) {
fprintf(stderr, "error in getting device descriptor\n");
return 1;
}
printf("bcdUSB: %04x\n", desc.bcdUSB);
For some devices I get 0401 value:
bcdUSB: 0401
I don't understand what's exactly the meaning of this value.
In libusb code I found this comment in libusb_device_descriptor structure code:
/** USB specification release number in binary-coded decimal. A value of
* 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */
uint16_t bcdUSB;
It specifies just 0200 and 0110 values meaning. Is there a documentation of all possible values of bcdUSB including 0401 ?
I am unaware about any documentation with all possible values of bcdUSB being described, yet have to mention one thing. Nothing prevents a USB device from sending an invalid device descriptor content. Although, i didn't exactly test it on anything, it seems quite likely to me an OS is going to ignore a wrong bcdUSB, with the device continuing to operate as expected.
Make sure to have some sane defaults, just in case of encountering an invalid value there.
Just to demonstrate, this is how a device descriptor is defined on a device side. Pretty much "hardcoded". And yea, this is an actual code, from an actual lib, running on an actual device.
/*-----------------------------------------------------------------------------+
| Device Descriptor
|-----------------------------------------------------------------------------*/
uint8_t const abromDeviceDescriptor[SIZEOF_DEVICE_DESCRIPTOR] = {
SIZEOF_DEVICE_DESCRIPTOR, // Length of this descriptor
DESC_TYPE_DEVICE, // Type code of this descriptor
0x00, 0x02, // Release of USB spec
0x02, // Device's base class code
0x00, // Device's sub class code
0x00, // Device's protocol type code
EP0_PACKET_SIZE, // End point 0's packet size
USB_VID&0xFF, USB_VID>>8, // Vendor ID for device, TI=0x0451
// You can order your own VID at www.usb.org"
USB_PID&0xFF, USB_PID>>8, // Product ID for device,
// this ID is to only with this example
VER_FW_L, VER_FW_H, // Revision level of device
1, // Index of manufacturer name string desc
2, // Index of product name string desc
USB_STR_INDEX_SERNUM, // Index of serial number string desc
1 // Number of configurations supported
};
Related
I have the following weird problem.
I have setup the BBB to activate the spi1 module. The the module is connected to an F-RAM chip (FM25CL64B). I have done all the necessary configuration. The /dev/spidev1.0 is present and I wrote a small program to write to, and read from the chip, by opening the /dev/spidev1.0 and using ioctl with the SPI_IOC_MESSAGE macro command.
Using that program I managed to successfully write 32 bytes of text in to the F-RAM chip. Reading also seemed to be successful... How do I know they both were successful? I used a logic analyzer with an SPI decoder activated to actually see what's going on over all four of the SPI lines. Monitoring all SPI lines I could see that the write and read operations generate the correct signals with the correct timings, and all signals are in sync. The CS enables the chip during the transaction, CLK clocks every byte in 8-bit words (as configured), the data lines show the correct values, which I could see thanks to the SPI decoder which shows the byte value right above each 8-bit signal sequence of both MOSI and MISO lines.
The problem is that even though I can see that correct information is sent over the MISO line during read operation, the buffer I provide to the ioctl(iSPIR, SPI_IOC_MESSAGE(2), xfer) is filled with zeros.
I intentionally initialized that buffer with other values, so I can see if the ioctl even writes into it. And it does. Zeros.
Now, the fact that I could see all the bytes sent over the MISO line during the read operation, proves that the writing operation not only looked right in the analyzer but actually wrote the intended data during the previous write operation.
I checked several times whether the MISO line is configured correctly, in the dts file (which I can rebuild and reinstall on demand). I checked if it's the correct pin, and if it's configured as input. Everything seemed to be correctly configured.
I ran the program as root in case there are permission issues - no difference.
I also implemented the spi communication in GPIO mode. I.e. the spi module is disabled, all lines configured as GPIO. CE, CLK, MOSI configured as otuputs and the MISO configured as input. This way I could implement the entire communication in software so I could have full control over the lines. Doing that, this time, I was able to successfully fill a buffer with the correct data from the F-RAM chip. I.e. the sequential read operation went fine from the F-RAM chip all the way to my user space buffer. I was able to print the data into the console. However, that worked way too slow. Also I find it inefficient to use purely software implementation of SPI com when there is a module available for use.
In order to write my sample program I used the spi_test.c open source example available online.
I also built and ran spi_test.c itself with no modification, same result.
Here is listing of my program (Relevant snippets):
// SPI config ...
int InitSPIReadMode(const char* pstrDeviceF)
{
int file;
__u8 wr_mode = SPI_MODE_0, rd_mode = SPI_MODE_0, lsb = 0, bits = 8;
__u32 speed = CLOCK_FREQ_HZ; // 500kHz
if((file = open(pstrDeviceF, O_RDWR)) < 0)
{
printf("Failed to open the bus.");
/* ERROR HANDLING; you can check errno to see what went wrong */
exit(1);
}
if(ioctl(file, SPI_IOC_RD_MODE, &rd_mode) < 0)
{
printf("SPI rd_mode\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0)
{
printf("SPI rd_lsb_fist\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0)
{
printf("SPI bits_per_word\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0)
{
printf("SPI max_speed_hz\n");
return -1;
}
printf("%s: spi wr-mode=%d, spi rd-mode=%d, %d bits per word, %s, %d Hz max\n", pstrDeviceF, wr_mode, rd_mode, bits, lsb ? "(lsb first) " : "(msb first)", speed);
xfer[0].cs_change = 0; /* Keep CS activated */
xfer[0].delay_usecs = 0; //delay in us
xfer[0].speed_hz = CLOCK_FREQ_HZ; //speed
xfer[0].bits_per_word = 8; // bites per word 8
xfer[1].cs_change = 0; /* Keep CS activated */
xfer[1].delay_usecs = 0;
xfer[1].speed_hz = CLOCK_FREQ_HZ;
xfer[1].bits_per_word = 8;
return file;
}
In the main function: (as the logic analyzer shows this code correctly sends the command, address and clocks the 32 bytes of data afterwards)
int iSPIR = InitSPIReadMode("/dev/spidev1.0"); //open("/dev/spidev1.0", O_RDWR | O_SYNC);
char arrInstruct[3] = { OPCO_READ, 0x00, 0x00 };
char arrFRamData[512];
for(int pos = 0; pos < 512; pos++) arrFRamData[pos] = pos;
xfer[0].tx_buf = (unsigned long)arrInstruct;
xfer[0].len = 3;
xfer[1].rx_buf = (unsigned long)arrFRamData;
xfer[1].len = 32;
if(ioctl(iSPIR, SPI_IOC_MESSAGE(2), xfer) < 0) printf("ioctl write error %s.\n", strerror(errno));
// hex dumping of the arrFRamData buffer.
xfer is a global variable defined as:
struct spi_ioc_transfer xfer[2];
Thanks a lot in advance! :)
I found what the problem was with my setup. But even though the whole thing works now, the solution I applied raises more questions than answers.
So I found this solution in an article (https://elinux.org/BeagleBone_Black_Enable_SPIDEV) on the beaglebone wiki.
There I noticed that, in the device-tree overlay they set the CLK as an input. Reading the entire article turned out nothing as to why the CLOCK on the BBBs side has to be an input. Even though it's the master... The article only explains how to build and install the new DTBO in order to activate the SPI1 module.
So, even though it didn't make any sense to me, I though it wouldn't hurt to try changing the CLK line in my DTBO file form output to input to see what happens... And it worked! :O
Now, why is it so strange that the setup works like this. The BBB is supposed to be the SPI master, so its clock line should be an Output in order to drive this synchronous communication. That means the FM25CL64B chip must act as SPI slave. I just checked the datasheet and yes, the CLK on the chips side, is an input. So the CLK on the BBB must be an output. That's how I've configured the CLK pin on the BBB. As an output. And it didn't work.
This is why I'm so puzzled. So it works even though both ends of the CLK line are inputs?! Looking at the logic analyzers output, I can clearly see that the CLK line is being driven correctly. But if both the master an slave have inputs on that line there shouldn't be anything to generate those impulses?! There is nothing else connected to that line....
I am trying to use an lcd screen on my stm32F3discovery.
The screen is made of an lcd 16 characters on 2 lines and an I2C module.
Here is the link of the product:
https://www.aliexpress.com/item/32763867041.html?spm=a2g0s.9042311.0.0.27424c4dsV7dLS
On the back of the screen I can see written: QAPASS 1602A
On the chip of the I2C module I can see written: PCF8574T
Here is the datasheet of the chip:
https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
I tried to follow this tutoriel (the closest from what I am trying to do):
https://www.youtube.com/watch?v=1COFk1M2tak
I use the HAL library, the main function to send data is "HAL_I2C_Master_Transmit".
Here is the description of the function in "HAL_I2C_Master_Transmit":
#brief Transmits in master mode an amount of data in blocking mode.
#param hi2c Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
#param DevAddress Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
#param pData Pointer to data buffer
#param Size Amount of data to be sent
#param Timeout Timeout duration
#retval HAL status
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
//I initialise a buffer
//I use a for loop to find the address of my slave: 0x3D (even tho on the //datasheet it's 0x3F, looks like A1 is bridged :O )
//I use the HAL_I2C_Master_Transmit function
//I move the address one bit to the left
//I reuse the HAL_I2C_Master_Transmit
//Nothing happens on the screen
//Here is my code (I tried to remove the useless comments):
#include "main.h"
I2C_HandleTypeDef hi2c1; // Init generated bu CubeMX
SPI_HandleTypeDef hspi1; // Init generated bu CubeMX
PCD_HandleTypeDef hpcd_USB_FS; // Init generated bu CubeMX
uint16_t adresseLCD; // the variable I put the slave address on
uint8_t buffer[]="123"; // The buffer I wanna see on the screen
void SystemClock_Config(void); // Generated by CubeMX
static void MX_GPIO_Init(void); // Generated by CubeMX
static void MX_I2C1_Init(void); // Generated by CubeMX
static void MX_SPI1_Init(void); // Generated by CubeMX
static void MX_USB_PCD_Init(void); // Generated by CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
adresseLCD=0x3D;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
adresseLCD=adresseLCD*2; // Yes I could have used "adresseLCD<<1" but I
//am not used to that
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
while(1)
{
}
}
I expected something to show on the screen (even random values) but nothing appears (it lights up though).
I get no error (only warnings because I use "1" instead of "Pin_ON" when I WritePIn)
You expect it wrong.
First of allŠ± the I2C module, which is soldered on back, it is just simple serial-to parallel convertor. When you write data on I2C bus, it sets its 8 outputs according to 8 received bits in data bytes. When you read data bytes, it switches into input mode and read logical levels on 8 pins and transmits it over serial wire. More detailed description you can read in the datasheet on PCF8574 you have provided.
I.e. this part does not perform any "magic" which will take characters on input and output them on display. That means the outputting of random data is futile.
You need to know two things more:
how this module is connected to the display driver
what kind of display driver is used and how to utilize it.
Answer to the first question may be found in the Internet:
(taken from here)
You can see the display is connected in 4-bit mode, outputs P4-P7 (i.e. what you transmit in four most significant bits of data bytes) are connected to data lines of the display, while outputs P0-P2 are connected to control lines RS, R/W, EH and P3 is used to control the backlight.
Knowing this we came to the second question. I can only suggest, but more likely your display module have Hitachi HD44780 IC on it. In the datasheet on it you can found the information what data should be output on the control lines.
You can find pin description at page 8:
bit RS selects whatever it be a command (0) or a data (1)
bit R/W chooses write opertion (0) or read (1)
and bit E is actually a strobe. On the falling edge, i.e. when it changes from 1 to 0, the display driver reads data from data lines. That means to pass 4 bits of data you should perform 2 write operations: first one with bit 2 is set high, second with all other bits are the same, but the bit 2 is zero.
Now you can read list of instructions at page 25 of datasheet. And initialization sequence for the 4-bit mode at page 46 (Figure 24). Note for each line of bits there you actually send 2 data bytes: with bit 2 high and then with bit 2 low.
Note, in 4-bit mode all commands and data consist of two write phases: first - top half, then bottom half of a byte. Each phase is 2 data writes to I2C module, with E bit high and low, i.e. you'll need to send 4 bytes to output 1 byte of data.
So basically in order to transmit data from you STM32 to the LCD display driver (HD44780) you need to emulate the latter's interface signalling via the I2C interface chip (PCF8574).
In other words, MCU will send I2C commands that will toggle the I2C "backpack" chip such that it should emulate the right signaling for the LCD driver.
This happens somehow easy whne you are using the HAL_I2C_Master_*() methods. In the buffer array you specify the state of the pins on the LCD as you want them and in the order [0], [1], [2]... etc. For example, let's say that we have DB[7:4] connected to the upper 4 bits of the PCF I2C expander. We can setup the following:
buffer[0] = 0xD0 ; // 0b11010000;
buffer[1] = 0xA0 ; // 0b10100000;
buffer[2] = 0xF0 ; // 0b11110000;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 3, 10); // Note 3 bytes are sent
Once the buffer is prepared, the HAL_I2C_Master_Transmit() call will send the tree bytes consecutively, thus toggling the DB pins as you have mentioned:
DB7: 1 -> 1 -> 1
DB6: 1 -> 0 -> 1
DB5: 0 -> 1 -> 1
DB4: 1 -> 0 -> 1
The same can be applied to all 8 pins on the PCF chip. It is also worth noticing that this sequential IO update also creates a bit less of I2C communication overhead as it only addresses the I2C slave chip once :) .
By the way, I am currently working on a LCD over I2C Library for STM32F1xx (probably it will fit other STM32F series). You can check it out on github (still WIP):
https://github.com/meteowrite/stm32_i2cLcd
Cheers
I am trying to initialize an SD card using an SPI bus and STM32F4 Discovery Board. I am mainly relying on Elm Chan's implementation of the disk_initialize function in the example code to base my own implementation. Unfortunately, I have run into an issue where sending CMD58 to the SD card during the initialization process return a result of 0x01, which implies that the SD card is idle. However, I am still seeing the next four bytes from the SD card as 0x00, 0xFF, 0x80, 0x00 which is in the right format for an R3 response. However, I am not sure if I can trust these four bytes as my OCR.
As of now, I have tried ignoring that the SD card is idling and simply tried to use the next four bytes as the OCR but the code seems to fail at other points during the mounting process with respect to the type of the card being assumed from the OCR.
if (Timer1 && SD_SendCmd(CMD58, 0) == 0) {
for (n = 0; n < 4; n++) {
ocr[n] = SPI_RxByte();
}
type = (ocr[0] & 0x40) ? 6 : 2;
}
The code segment above is where I am first seeing the idle response. SD_SendCmd is where I send CMD58 to the SD card and where I am receiving 0x01 as the leftmost byte of the five byte response. Because I am not receiving 0x00, which signals that the SD card has no issues with the command passed to it, the code breaks out of the initialization process and returns an error. I would greatly appreciate any help with this matter as I have been stumped by this 0x01 return value for quite some time now. Thank you!
So I was able to figure out the issue. It turns out that the card I was using was an SDHC card, where HC stands for high capacity. According to the simplified SD card specification, the CRC, which is sent at the end of the transmission of a command has to have the least significant bit set to 1. So, ORing the CRC with 0x01 before any of the transmissions let me initialize and use any type of SD card. So the issue I was having was not from CMD58, but how I was dealing with the CRC in general. Interestingly enough, not ORing the CRC seems to work fine with non high-capacity SD cards. But ORing the CRC with 0x01 seems to work with all cards (at least as far as I have tested).
With regard to ORing the CRC with 0x01, this is not the CRC, but the STOP-BIT, the CRC is of type CRC7 and is at bits 1:7 of byte #6 in the command. According to the specification, even if the CRC is not required, the stop bit must always be 1.
I am taking a shot at writing a wrapper for the S1V30120 dectalk text synthesis IC in C language using the ESP IDF. I am running into a problem in the following code.
printf("Hello world!\n");
esp_err_t ret;
spi_device_handle_t spi;
spi_bus_config_t buscfg={
.miso_io_num=PIN_NUM_MISO,
.mosi_io_num=PIN_NUM_MOSI,
.sclk_io_num=PIN_NUM_CLK,
.quadhd_io_num=-1,
.quadwp_io_num=-1
};
spi_device_interface_config_t devcfg={
.clock_speed_hz=750000,
.mode=0,
.spics_io_num=PIN_NUM_CS,
.queue_size=7
};
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
assert(ret==ESP_OK);
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
uint8_t rec[20];
assert(ret==ESP_OK);
uint8_t cmd[] = {0xAA, 0x04, 0x00, 0x05, 0x00}; // get information command
printf("waiting for rdy");
while (gpio_get_level(PIN_NUM_RDY) == 0){};
//create 2 transactions 1 for transmitting the GET INFORMATION command
//and 1 for getting the data back from the ic
spi_transaction_t t;
spi_transaction_t r;
memset(&t, 0, sizeof(t));
t.length=5*8;
t.tx_buffer=&cmd;
r.length=20*8;
r.rx_buffer=&rec;
ret = spi_device_transmit(spi, &t);
assert( ret == ESP_OK);
ret = spi_device_transmit(spi, &r);
assert(ret == ESP_OK);
printf((char*)r.rx_data);
/* Print chip information */
printf("Restarting now.\n");
I'm pretty sure connection should be in full duplex mode and I believe that is set up correctly. the information coming back should be 20 bytes but I am getting the error
rxdata transfer > 32 bits without configured DMA
at the moment I am following 2 pieces of code that may help.
an example of using SPI in the esp idf:
https://github.com/espressif/esp-idf/blob/3276a1316f66a923ee2e75b9bd5c7f1006d160f5/examples/peripherals/spi_master/main/spi_master_example_main.c
an example of using the dectalk ic in Arduino ide:
https://electronza.com/arduino-due-s1v30120-text-speech-code/2/
the dectalk ic protocol sheet:
https://github.com/MikroElektronika/Click_TextToSpeech_S1V30120/blob/master/datasheet/S1V30120%20Protocol%20Specification.pdf
and the SPI documentation for the esp idf:
https://gitdemo.readthedocs.io/en/latest/api/peripherals/spi_master.html
the protocol sheet also says something about sending 16 bytes of 0x00 after a transaction I've never seen that before though.
I hope I was thorough enough with all this information and I thank anyone in advance who can help!
You have used "1" for DMA in the line
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
But as mentioned in the docs, the DMA needs to be configured.
For external PSRAM available on these boards that means something like
pvPortMallocCaps(20, MALLOC_CAP_DMA)
Without DMA only 32 bits data are possible per transaction.
I'm writing a device-side USB driver that uses the USB module on a Freescale Kinetis K20 ARM Cortex-M4 processor. On the host side I am running Arch on an x64 processor.
The issue I am having is that I cannot seem to get linux to read the descriptor of my device. My device has one configuration with one interface and no endpoints (just the control). My device descriptor looks like so:
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} dev_descriptor_t;
static const dev_descriptor_t dev_descriptor = {
.bLength = 18,
.bDescriptorType = 1,
.bcdUSB = 0x0100,
.bDeviceClass = 0xff,
.bDeviceSubClass = 0x0,
.bDeviceProtocol = 0x0,
.bMaxPacketSize0 = ENDP0_SIZE,
.idVendor = 0x16c0,
.idProduct = 0x05dc,
.bcdDevice = 0x0001,
.iManufacturer = 0,
.iProduct = 0,
.iSerialNumber = 0,
.bNumConfigurations = 1
};
Assuming the processor reads the bytes starting at bLength I would think this descriptor would work (I have corresponding configuration and interface descriptors, but it isn't even getting that far).
The errors I get is as follows:
usb 4-1.4: device descriptor read/64, error 18
...repeated 4 times
usb 4-1.4: device descriptor read/8, error -75
..repeated 4 times
usb 4-1-port4: unable to enumerate USB device
I've managed to find a list of error codes and -75 is EOVERFLOW which makes sense since my descriptor wouldn't fit inside an 8 byte read. The one that really confuses me is error 18.
My Question:
What is error 18 and what causes it?
Just to be clear: My question isn't about getting the USB module to work on a Kinetis microcontroller (although any hints and experience would be appreciated)...its about finding out what this error code means and diagnosing the problem that causes it.
Error -18 (note the negative) is EXDEV (Cross-link device) which makes no sense to me because I don't know what it means.
Note 1
I know that there isn't a hardware issue with the USB module since the microcontroller is part of a Teensy 3.1 board and I have used its USB module in past projects, but using the provided driver that comes with the accompanying Teensyduino library. I'm writing my own to understand the module better.
Note 2
If its helpful to know, the microcontroller is receiving the command to be assigned an address and seems to respond correctly (i.e. no "not accepting address" errors in my log...I already worked through those). Other than that and the get descriptor command, it doesn't seem to be receiving any additional commands.
18 is not an error here. Note it is a positive number, while all the error codes are converted to negative numbers in Linux kernel.
Here, 18 is the return value of usb_control_msg(), which returns the length of the device descriptor if success. So it's the bLength field in your device descriptor, which is 18.
The problem lies in the bMaxPacketSize0 field. I don't know what ENDP0_SIZE is, but usb core only accepts following values: 8, 16, 32, 64, 255. If it does not match, the usb_control_msg() is considered failure and report error.
Check hub_port_init() in drivers/usb/core/hub.c. The code flow should be clear.