HID report not working while changed length - c

I'm building a custom keyboard with stm32f103.
My first trial with standard 8 byte works pretty well:
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
//Modifiers
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xe0, // Usage Minimum (224)
0x29, 0xe7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
//Reserveds
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input (Constant) reserved byte(1)
//Regular Keypads
0x95, 0x06, // Report Count (normally 6)
0x75, 0x08, // Report Size (8)
0x26, 0xff, 0x00,
0x05, 0x07, // Usage Page (Key codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0xbc, // Usage Maximum (188)
0x81, 0x00, // Input (Data, Array) Key array(6 bytes)
0xC0 // End Collection (Application)
then I tried to make the report length longer to support more key press at the same time, so I change this
0x95, 0x06, // Report Count (normally 6)
to this
0x95, 0x30, // Report Count (normally 6)
and accordingly
struct HIDreport
{
int8_t mod;
int8_t reserv;
int8_t key[lenth];
};
struct HIDreport report;
but I found any of the key press do not work, what am I missing?
thanks

If your Interface defines the keyboard as a "BOOT keyboard", for example:
0x03, /*bInterfaceClass: HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x01, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
...then I'm pretty sure the HID report descriptor will be ignored. The idea of a "BOOT keyboard" is that it uses a fixed size buffer (1 byte keyboard modifiers, 1 byte reserved, 6 bytes of keyboard usage indexes) so that it can be recognised during boot up (e.g. to modify CMOS settings) without having to implement a full USB stack in the BIOS.
Valid combinations of Class, Subclass, Protocol are as follows:
Class Subclass Protocol Meaning
3 0 0 Class=HID with no specific Subclass or Protocol:
Can have ANY size reports (not just 8-byte reports)
3 1 1 Class=HID, Subclass=BOOT device, Protocol=keyboard:
REQUIRES 8-byte reports in order for it to be recognised by BIOS when booting.
That is because the entire USB protocol cannot be implemented in BIOS, so
motherboard manufacturers have agreed to use a fixed 8-byte report during booting.
3 1 2 Class=HID, Subclass=BOOT device, Protocol=mouse
The above information is documented in Appendix E.3 "Interface Descriptor (Keyboard)"
of the "Device Class Definition for Human Interface Devices (HID) v1.11" document (HID1_11.pdf) from www.usb.org
Edit: The use case for a buffer size of more than 6 keys is questionable anyway because the buffer represents those keys that are simultaneously pressed at a particular point in time. It is unlikely that anyone would need to press more than 6 keys simultaneously.

Related

Why can't I have an HID descriptor for a gamepad with 17 buttons?

I currently have this HID report descriptor:
static
unsigned char hid_report_descriptor[] __attribute__ ((aligned(64))) = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0xA1, 0x00, // Collection (Physical)
0x85, 0x01, // Report ID (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x10, // Report Count (16)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x33, // Usage (Rx)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
};
It corresponds to this struct.
struct GamepadReport {
uint8_t report_id;
uint16_t buttons;
int8_t left_x;
int8_t left_y;
int8_t right_x;
int8_t right_y;
} __attribute__((packed));
I'm trying to add support for a single extra button that should serve as the "home" button (think of the X on an Xbox controller). This, in theory, should be done by changing the lines containing 0x29, 0x10 and 0x95, 0x10 to 0x29, 0x11 and 0x95, 0x11 respectively. However, doing so breaks the connection with the custom controller.
I cannot for the life of me figure out why this is and it makes absolutely zero sense to me. Can someone with more experience or knowledge about HID descriptors give me a hand?
In case anyone stumbles upon this or has a similar issue. You can't create a report count field on hid descriptors with numbers not divisible by 8 unless you add padding bits.
The solution was straightforward after reviewing the comments on my question and looking at similar issues online.
My gamepad report struct could only hold 16 bits. Even if I had a correctly defined hid descriptor, this would've prevented it from working. I changed my struct to the following.
struct GamepadReport {
uint8_t report_id;
uint32_t buttons;
int8_t left_x;
int8_t left_y;
int8_t right_x;
int8_t right_y;
} __attribute__((packed));
Modify your hid descriptor to contain the padding bits to the next number divisible by 8 that fits within your struct types. In this case, I need to fill 32 bits and I have 17 buttons. 32 - 17 means I need to add 15 padding bits.
0x75, 0xF, // Report Size (15) - PADDING BITS
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

Shellcode crashing target PE

I am currently attempting to patch a target x86 PE file from the disk with a tool, patch.exe.
purpose
The purpose of this tool will be to eventually write/insert a multi-function payload into the target executable, who's purpose is to track the position of certain frames inside a game which I created.
background
I am doing this by mapping the file into memory with PAGE_READWRITE protection flag. After locating the RVA of foobar(...) function from memory, I am replacing the call to this function with a JMP (0xE9) instruction followed by the RVA of the payload shellcode (which is previously appended to a new section I have created within the target PE file beforehand).
When the shellcode simply contains the following bytes
unsigned char shellcode[16] = {0x33, 0xc0, 0xc3, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc}
which translates to the following C function
unsigned long basic_ret() { return 0; }
the target executable successfully executes
problem
however, if I try to include another function (0xDEADBEEF) within the code
unsigned char shellcode[32] =
{
0x6a, 0x0b, 0x68, 0x9c, 0xc2, 0xf4, 0x00, 0x6a, 0x0a, 0x68, 0xa8, 0xc2, 0xf4, 0x00, 0x68, 0xb4,
0xc2, 0xf4, 0x00, 0xb8, 0xef, 0xeb, 0xda, 0xed, 0xff, 0xd0, 0x83, 0xc4, 0x14, 0xc3, 0xcc, 0xcc
};
which translates to the following C function (a simple JMP taking 3 arguments on the stack, to another function)
int simple_jmp()
{
typedef int (*_jmp_target)(void*, int, void*);
_jmp_target jmp_target = (_jmp_target) 0xDEADBEEF;
return jmp_target (0, 5, 0);
}
where jmp_target is filled by patch.exe with the RVA to a function previously referred to, which was inserted into the new section of the target PE.
When the the target executable is executed, this time, it reaches some type of violation/crash (confirmed by the presence of WerFault.exe alongside its execution).
What is the reason for this problem, what am I missing?
diagnostics
When I observe the patched bytes in memory (of the mapped PE file), I can see that 0xdeadbeef is replaced by 0x0dc3f4de which is the same address from which the inserted function shellcode starts. At this point, I assumed there would be no crash, little to my surprise - crash!

Cannot figure out how to send data to hat switch from stm32f103c8 to PC via USB

I have recently restarted to play around with micro controllers and finally got kinda stuck. So what I am building is a custom game pad. I can simulate data correctly for buttons but nothing works when I bring in the hat switch. I assume I am sending wrong data packet but cannot figure out the correct structure. In the test code I am just trying to send some "button press" and also trying to press down a key from hat switch, but it looks like that pc cannot recognise my data packet. I did go through the hid documentation (especially page 64, 65) and still have no idea why this is not working.
My HID descriptor:
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x0e, // USAGE_MAXIMUM (Button 14)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x0e, // REPORT_COUNT (14)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x02, // REPORT_SIZE (2)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x03, // LOGICAL_MAXIMUM (3)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x0e, 0x01, // PHYSICAL_MAXIMUM (270)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x04, // REPORT_SIZE (4)
0x81, 0x42, // INPUT (Data,Var,Abs,Null)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x04, // REPORT_SIZE (4)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
Basic test code:
struct gamepad_report_t
{
uint16_t buttons;
uint8_t hs0 : 1;
uint8_t hs1 : 1;
uint8_t hs2 : 1;
uint8_t hs3 : 1;
};
struct gamepad_report_t gamepad_report;
gamepad_report.buttons = 0x0001;
gamepad_report.hs0 = 1;
gamepad_report.hs1 = 0;
gamepad_report.hs2 = 0;
gamepad_report.hs3 = 0;
int main()
{
while (1)
{
gamepad_report.buttons = 0x0010;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, &gamepad_report, sizeof(struct gamepad_report_t));
HAL_Delay(500);
}
}
Data packet structure I have imaganed
What I see in the device properties when the uC is connected
The problem I struggled here was the struct variable I wanted to send to the PC. If I am sending an array, everything works as expected... so the conclusion is that sizeof(struct) is not returning correct value, I should read through data structure alignment ... typically simple issue what can drive people crazy.
First thing I see wrong is that you are trying to use the hat switch like buttons. The hat switch has a value for each direction.
For example Up-Left has its own value, it is not a combination of Up and Left being pressed.
You will have to read your buttons, and then encode them as a value.
It can be a little finicky to set up for the first time.
Here are the values for each button, which should clear up any confusion. (Won't work with every report descriptor!)
#define HATSWITCH_UP 0x00
#define HATSWITCH_UPRIGHT 0x01
#define HATSWITCH_RIGHT 0x02
#define HATSWITCH_DOWNRIGHT 0x03
#define HATSWITCH_DOWN 0x04
#define HATSWITCH_DOWNLEFT 0x05
#define HATSWITCH_LEFT 0x06
#define HATSWITCH_UPLEFT 0x07
#define HATSWITCH_NONE 0x0F
And here is part of a descriptor that will work with those values.
Just define a uint8_t for the hat switch in the report descriptor and you should be good to go.
0x05, 0x01, //Usage Page : Generic Desktop
0x09, 0x39, //Usage : Hat Switch,
0x15, 0x00, //Logical Min : 0
0x25, 0x07, //Logical Max : 7
0x46, 0x3B, 0x01, //Physical Maximum : 315 degrees (Optional)
0x75, 0x08, //Report Size : 8
0x95, 0x01, //Report Count : 1
0x65, 0x14, //Unit : English Rotation/Angular Position 1 degree (Optional)
0x81, 0x42, //Input : Data, Var, Abs, Null State
I know this is an old topic, and you most likely figured it out, but it will help people in the future.

why does this make my display go mad? (but only sometimes)

it seems this code make my display go crazy sometimes (but only sometimes). But when I remove dat=~dat; it seems to work fine.
why?
what I am trying to do here is just make the ascii letters be the oposite: so for example:
11001000 will be:
00110111
or
10101111 would be:
01010000
the reason for doing this is that i want to have one row (the active row) in the diplay window with black on white pixels instead of opostie like the rest of the display window.
Is there some other way I could do this? (invert the numbers)
FYI: I am programing in C. Atmel studio. atmega 4809, SSD1305z display, SPI-simular interface.
void displayinvertedString(char str[], uint8_t ypos,uint8_t xpos)
{
Set_Page_Address(ypos);
Set_Column_Address(xpos);
int len = strlen(str);
uint8_t dat;
int temp;
for (int e=0; e<len; e++)
{
dat = 0xff;
Write_Data(dat); //to get an extra space between the
// numbers/letters for
//making it easier to read the text on the display
temp = str[e];
temp=temp-0x20; // As the lookup table starts from Space(0x20)
for (int w=0; w<5; w++)
{
dat= OledFontTable[temp][w]; // Get the data to be displayed for LookUptable
dat =~ dat;
Write_Data(dat);
}
}
}
----------
static uint8_t OledFontTable[][FONT_SIZE]={
//static uint8_t OledFontTable[] = {
0x00, 0x00, 0x00, 0x00, 0x00, // space
0x00, 0x00, 0x2f, 0x00, 0x00, // !
0x00, 0x07, 0x00, 0x07, 0x00, // "
0x14, 0x7f, 0x14, 0x7f, 0x14, // #
0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
0x23, 0x13, 0x08, 0x64, 0x62, // %
0x36, 0x49, 0x55, 0x22, 0x50, // &
ETC. Etc.
just more raw pixel data here. this part ends like this:
0x00, 0x00, 0xFF, 0x00, 0x00, // |
0x00, 0x82, 0x7C, 0x10, 0x00, // }
0x00, 0x06, 0x09, 0x09, 0x06 // ~ (Degrees)
};
void Write_Data(unsigned char Data)
{
PORTA.OUTCLR = PIN7_bm; //cs
PORTB.OUTSET = PIN2_bm; //dc
Write_Command(Data); //
}
void Write_Command(unsigned char data)
{
SPI0.DATA = data; // copy data to DATA register
while ((SPI0.INTFLAGS & SPI_RXCIF_bm) == 0) ; //wait
}
I have asked a bit about this before. but i thought i would look "cleaner" with a new tread since info was missing from the last one.
It turned out I needed to toggle the chip select (CS) so the clock did not get out of sync with time.
The clock sync drifted with time.
It was going crazy faster for the non inverted data for some reason. But with the normal data it happend after some time also.
Thank you for the answers.

ARM: reading modbus data through UART fails

I am trying to read a register from an RS485 adapter using a UART connection on an ARM board with no success
Machines
ARM board: CM-T335 - TI AM335x Computer-on-Module http://www.compulab.co.il/products/computer-on-modules/cm-t335/
PC: x86 machine running Ubuntu
Slave device: Elnet Pico (Energy power meter) - works as a modbus slave and sends energy statuses to master when requested using the modbus protocol http://elnet.feniks-pro.com/documents/User_manual/Elnet%20Pico%20-%20User%20Manual.pdf
Connectors
RX/TX cable
SENA RS485 adapter: http://www.senanetworks.com/download/datasheet/ds_ltc100.pdf
Male - Male COM adapter: http://i.ebayimg.com/images/i/300758549547-0-1/s-l1000.jpg
ultra mini serial to DB9 cable: https://cdn3.yawarra.com.au/wp-content/uploads/Ultra-mini-serial-DB9-adapter_600x600.jpg
Slave device connected to ARM board
Energy power meter <--rx/tx cable--> SENA RS485 Adapter <--Serial Male/Male adapter + Serial to mini serial cable --> ARM board mini-serial port
see images of the setup:
https://ibin.co/3CCvTVPPIUWQ.jpg
https://ibin.co/3CCvhrr3CQjr.jpg
https://ibin.co/3CCw5uNqTW8B.jpg
https://ibin.co/3CCvuys1tUgt.jpg
Device tree used: am335x-sbc-t335.dts
Device tree resources: http://get-album.com/dts/
Slave device connected to PC
Energy power meter <--2 wire rx/tx--> SENA RS485 Adapter <--> PC
note: The mini serial port on the board is the same port that is used for serial-console communication (on baud 115200) - and this works with no problems
Below is a c code that reads the first register from the connected slave device with the use of libmodbus library
libmodbus_test.c - reading and printing the first reigster from the device:
#include <sys/types.h>
#include <string.h>
#include <modbus.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MODBUS_DEVICE "/dev/ttyS2"
#define MODBUS_STOP_BIT 1
#define MODBUS_DATA_BIT 8
#define MODBUS_START_BIT 1
#define MODBUS_PARITY 'N'
int main(int argc, char *argv[]) {
const char *dev_path = MODBUS_DEVICE;
uint16_t result[2];
int slave_nr = 31, baud = 9600;
modbus_t *mb;
if (argc == 2) {
dev_path=argv[1];
}
mb = modbus_new_rtu(dev_path, baud, MODBUS_PARITY, MODBUS_DATA_BIT, MODBUS_STOP_BIT);
if (mb == NULL) {
printf("error creating libmodbus rtu\n");
return -1;
}
printf("created new rtu...\n");
modbus_set_debug(mb, 1);
if (modbus_connect(mb) < 0 ){
printf("modbus error: %s\n", modbus_strerror(errno));
modbus_close(mb);
modbus_free(mb);
return -1;
}
modbus_set_slave(mb, slave_nr);
printf("ModBus connected !\n");
if (modbus_read_registers(mb, 0x1, 2, result) < 0) {
printf("error reading bits: %s\n", modbus_strerror(errno));
modbus_close(mb);
modbus_free(mb);
return -1;
}
printf("successfully red integer: %d: %X (hex)\n", result[0], result[0]);
modbus_free(mb);
return 0;
}
[output from running libmodbus_test on PC]
root#cm-debian:~/new# modbus gcc -I /usr/local/include/modbus libmodbus_test.c -o libmodbus_test -lmodbus
root#cm-debian:~/new# ./libmodbus_test /dev/ttyS2
created new rtu...
Opening /dev/ttyS2 at 9600 bauds (N, 8, 1)
ModBus connected !
[1F][03][00][01][00][02][96][75]
Waiting for a confirmation...
<1F><03><04><00><DD><00><DD><54><51>
successfully red integer: 221: DD (hex)
[output from running libmodbus_test on the ARM board]
root#cm-debian:~/new# gcc -I /usr/include/modbus/ libmodbus_test.c -o libmodbus_test -lmodbus
root#cm-debian:~/new# ./libmodbus_test /dev/ttyO0
created new rtu...
Opening /dev/ttyO0 at 9600 bauds (N, 8, 1)
ModBus connected !
[1F][03][00][01][00][02][96][75]
Waiting for a confirmation...
ERROR Connection timed out: select
when executing libmodbus_test from the ARM board, select() always returns 0
while running the same program on PC, it works just fine => the slave device returns data.
An attempt using termios also failed with similar results
termios_test.c
#include <sys/select.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <linux/serial.h>
#include <sys/ioctl.h>
static const uint8_t table_crc_hi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
/* Table of CRC values for low-order byte */
static const uint8_t table_crc_lo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
void calc_crc(uint8_t *buffer, ssize_t length, uint8_t *crc_hi_arg, uint8_t *crc_lo_arg) {
uint8_t crc_hi = 0xff;
uint8_t crc_lo = 0xff;
unsigned int i;
while (length--) {
i = crc_hi ^ *buffer++;
crc_hi = crc_lo ^ table_crc_hi[i];
crc_lo = table_crc_lo[i];
}
*crc_hi_arg = crc_hi;
*crc_lo_arg = crc_lo;
}
int main(int argc, char **argv){
char *dev_path = "/dev/ttyS2";
if (argc == 2) {
dev_path = argv[1];
}
uint8_t write_data[8];
int fd,write_len,select_ret, bytes_avail, status;
struct termios config;
char c;
uint8_t crc_hi, crc_lo;
fd_set activefs, tmpfs;;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec= 500000;
fd = open(dev_path, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
if (fd == -1){
perror("open");
return 1;
}
FD_ZERO(&tmpfs);
FD_SET(fd, &tmpfs);
printf("opened device\n");
if (tcgetattr(fd, &config) < 0) { close(fd); return -1 }
if (cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) {
printf("cant setting speed!\n");
close(fd);
return 1;
}
config.c_cflag |= (CREAD | CLOCAL);
config.c_cflag &=~ CSIZE;
config.c_cflag &=~ CSTOPB;
config.c_cflag &=~ PARENB;
config.c_cflag |= CS8;
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
config.c_iflag &= ~INPCK;
config.c_iflag &= ~(IXON | IXOFF| IXANY);
config.c_oflag &= ~OPOST;
config.c_cc[VMIN] = 0;
config.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &config) < 0) {
printf("cant apply config!\n");
close(fd);
return 1;
}
write_data[0] = 0x1f; // slave addr
write_data[1] = 0x03; // function
write_data[2] = 0x00; // data address of first coil (8b)
write_data[3] = 0x01; // data address of first coil (8b)
write_data[4] = 0x00; // num of coils requested
write_data[5] = 0x01; // num of coils requested
calc_crc(write_data, 6, &crc_hi, &crc_lo);
write_data[6] = crc_hi;
write_data[7] = crc_lo;
printf("data: [0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x]", write_data[0], write_data[1], write_data[2], write_data[3], write_data[4], write_data[5], write_data[6], write_data[7]);
while (1) {
sleep(1);
write_len= write(fd, write_data, 8);
activefs = tmpfs;
select_ret = select(1, &activefs, NULL, NULL, &timeout);
if (select_ret < 0) {
perror("select");
return 1;
}
if (select_ret > 0) {
printf("select returned %d\n", select_ret);
if (read(fd, &c, 1) < 0) {
perror("read");
} else {
printf("received: %d\n", c);
}
}
}
}
[output from termios.c on the ARM board]
root#cm-debian:~/new# ./termios /dev/ttyO0
opened device
... select keeps returning 0
[output from termios.c on PC]
$ gcc -o termios_test termios_test.c
$ ./termios_test /dev/ttyS2
opened device
data: [0x1f][0x3][0x0][0x1][0x0][0x1][0xd6][0x74]select returned 1
received: 31
select returned 1
Never mind the values, there IS a data exchange and that's what I want to achieve using the board
I tried passing RS485 attributes via pcntl, but the same results happened
termios_rs485_test.c: http://pastebin.com/RWtHtjLF
The connection between the board and the PC is done through the ultra mini serial to DB9 cable and I can successfully read/write data from and to the board. How come reading data from the RS485 adapter never succeeds? where should I be looking in order to get closer a solution?
Here are some info about the board / drivers / etc
root#cm-debian:~/modbus# uname -a
Linux cm-debian 4.4.0-cm-t335-5.1 #98 SMP Thu Sep 1 15:12:31 IDT 2016 armv7l GNU/Linux
root#cm-debian:~/new# dmesg | grep -i --color '\(serial\|tty\|uart\)'
[ 0.000000] Kernel command line: console=ttyO0,115200n8 root=ubi0:rootfs rw rootfstype=ubifs ubi.mtd=rootfs
[ 0.771007] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[ 0.780286] omap_uart 44e09000.serial: no wakeirq for uart0
[ 0.780329] of_get_named_gpiod_flags: can't parse 'rts-gpio' property of node '/ocp/serial#44e09000[0]'
[ 0.780960] 44e09000.serial: ttyO0 at MMIO 0x44e09000 (irq = 155, base_baud = 3000000) is a OMAP UART0
[ 1.543031] console [ttyO0] enabled
[ 1.550036] omap_uart 48022000.serial: no wakeirq for uart1
[ 1.556099] of_get_named_gpiod_flags: can't parse 'rts-gpio' property of node '/ocp/serial#48022000[0]'
[ 1.556764] 48022000.serial: ttyO1 at MMIO 0x48022000 (irq = 156, base_baud = 3000000) is a OMAP UART1
[ 2.953486] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 2.973176] usb usb1: SerialNumber: musb-hdrc.0.auto
[ 3.572722] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[ 6.689030] systemd[1]: Expecting device dev-ttyO0.device...
[ 7.210727] systemd[1]: Starting system-getty.slice.
[ 7.235407] systemd[1]: Created slice system-getty.slice.
[ 7.241302] systemd[1]: Starting system-serial\x2dgetty.slice.
[ 7.265277] systemd[1]: Created slice system-serial\x2dgetty.slice.
[ 7.925632] systemd[1]: Starting LSB: controls configuration of serial ports...
[ 8.485680] systemd[1]: Started LSB: controls configuration of serial ports.
[ 14.840532] pinctrl-single 44e10800.pinmux: pin 44e10978.0 already requested by 48022000.serial; cannot claim for 481cc000.can
[ 14.895866] pinctrl-single 44e10800.pinmux: pin 44e10980.0 already requested by 48022000.serial; cannot claim for 481d0000.can
root#cm-debian:~/modbus# setserial -a /dev/ttyO0
/dev/ttyO0, Line 0, UART: undefined, Port: 0x0000, IRQ: 155
Baud_base: 3000000, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root#cm-debian:~/new# setserial -a /dev/ttyS2
/dev/ttyS2, Line 2, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root#cm-debian:~/new# setserial -a /dev/ttyS1
/dev/ttyS1, Line 1, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root#cm-debian:~/new# setserial -a /dev/ttyS0
/dev/ttyS0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root#cm-debian:~/modbus# lsmod
Module Size Used by
sha256_generic 9731 1
hmac 2866 1
drbg 13731 1
ctr 3673 2
ccm 7928 2
arc4 2000 2
wl12xx 57190 0
wlcore 180594 1 wl12xx
mac80211 605465 2 wl12xx,wlcore
cfg80211 492985 2 mac80211,wlcore
snd_soc_davinci_mcasp 15953 2
snd_soc_tlv320aic23_i2c 2092 1
snd_soc_simple_card 7474 0
snd_soc_tlv320aic23 10191 1 snd_soc_tlv320aic23_i2c
snd_soc_edma 1309 1 snd_soc_davinci_mcasp
snd_soc_core 158330 5 snd_soc_davinci_mcasp,snd_soc_edma,snd_soc_tlv320aic23_i2c,snd_soc_tlv320aic23,snd_soc_simple_card
snd_pcm_dmaengine 5548 1 snd_soc_core
snd_pcm 92743 4 snd_soc_davinci_mcasp,snd_soc_core,snd_soc_tlv320aic23,snd_pcm_dmaengine
c_can_platform 6650 0
c_can 9638 1 c_can_platform
wlcore_spi 5086 0
can_dev 12315 1 c_can
ti_am335x_adc 5635 0
snd_timer 21413 1 snd_pcm
kfifo_buf 3452 1 ti_am335x_adc
snd 55936 3 snd_soc_core,snd_timer,snd_pcm
industrialio 40286 2 ti_am335x_adc,kfifo_buf
evdev 13187 0
omap_wdt 5293 0
soundcore 1339 1 snd
root#cm-debian:~/new# cat /proc/cpuinfo
processor : 0
model name : ARMv7 Processor rev 2 (v7l)
BogoMIPS : 597.60
Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x3
CPU part : 0xc08
CPU revision : 2
Hardware : Generic AM33XX (Flattened Device Tree)
Revision : 0000
Serial : 0000000000000000
root#cm-debian:~/new# cat /proc/consoles
ttyO0 -W- (EC p ) 250:0
root#cm-debian:/etc# cat debian_version
8.2
Any help is well appreciated
Thanks
UPDATE
While the ultra mini serial to DB9 cable is connected to the board, I ran termios_test.c and while running, I inserted a metal piece in the middle pin of the cable (the TX) and I could see garbage data showing on the screen (sometimes!). I plugged the male-male adapter to the cable and touched the middle pin again, and I could see garbage data again.
Another thing I've done is connecting the slave device to to the SENA RS485 adapter, and use the metal piece on the pins, the middle pin of the adapter, when touching the metal, makes the TX led turn on. no pins would turn on the RX led. When connecting that RS485 adapter to the PC's COM port, sending data to that port would make the RX led flash. I am beginning to suspect that the board is not writing to the RS485 adapter correctly, or that pins are not mapped correctly. Where should I go from here? might this be really a software issue?
There are two software issues found in the termios_test.c. First issue was related to missed tcgetattr(). Second one is what the first parameter to select() must be 1, not fd+1. Now they fixed and the code still don't work with SENA RS485.
Artless Noise suggested to connect ARM board to PC and check if UART on ARM board is properly configured. Smokie connected ARM to PC using ultra mini serial to DB9 cable and confirmed that termios_test.c can send and receive data to windows PC.
For now, I assumed than the problem is with physical connection of ARM to SENA. Ultra mini serial to DB9 cable used to connect ARM to PC is a crosscable. It connect ARM RX pin to PC TX pin and vice versa. SENA RS485 adapter is intended to be connected to PC directly without any cables. It has direct connetion: PC TX pin connected to SENA TX pin and PC RX pin to SENA RX pin. ARM is connected to SENA using ultra mini serial to DB9 cable and male - male COM adapter. Male - Male COM adapter doesn't change pin order, so TX pin became TX pin. So in the connection ARM TX pin became wrong connected to SENA RX pin.
In order to make schematic work, COM null modem cable must be used instead of Male - Male COM adapter. It basic pinout might be following:
ARM side SENA side
TXT pin 3 <--> RXD pin 2
RXD pin 2 <--> TXT pin 3
GND <--> GND
RTS pin 7 (leave unconnected) +-> RTS pin 7
|
CTS pin 8 (leave unconnected) +-> CTS pin 8
|
DTR pin 4 (leave unconnected) +--> DTR pin 4 (to emulate the same behavior as PC does)
DCD pin 1 (leave unconnected) (leave unconnected) DCD pin 1
DSR pin 6 (leave unconnected) (leave unconnected) DSR pin 6
If SENA use hardware flow control, then RTS and DTR inputs must be connected to CTS output. If not, they might left unconnected. I suggest to connect RXD, TXD and GND signals first, and if that won't work, connect RTS, CTS and DTR pins.
It is possible to connect RTS and CTS pins to ARM to implement hardware flow control, but that doesn't make sence. Because SENA RS485 TX/RX speed is the same as UART speed. Anyway, the following changes should be made: install R44 and R45 (which is not installed on ARM board) and modify RTS/CTS pins in the ARM board DTS file (now they used as I2C1 pins) and setup UART in software to use flow control.
In case if someone claim UART on ARM you will see unexpected characters in the the data flow (for example you will see SENA TX pin blinks during ARM boot, if ARM using UART0 as console). Or you will see 'login:' or 'login incorrect' stings in the data flow if getty using UART. Also ARM boot ROM could print some strings to UART and you cannot disable that behaviour. U-boot might use UART as console and output banner strings. Be ready that such data will go into RS485 and somehow affect it's work.

Resources