I have a function which receives 8-byte data on UART and I am using one of the data byte in a way that I read that data byte and call a function that modulates the volume of the MP3 codec that I am going to use.
The MP3 codec function that sets the volume is a little weird that it sets the sound as if the input to it is 0x00, the 100% volume and if it happens to be 0x64 then its 0%.
Now, the LabVIEW firmware is set up (by some other developer in team) in such a manner that if it wants to set the volume to be 100% then it sends 0x64, and 0x00 for 0%, through the UART into the data byte that I am going to read, as I said above.
Avoiding the complications on this post, what I have tried to do is, to implement a simple C code that would take the input from console and print the processed output. Say if input is 0x00, then the output should be 0x64 and if input happens to be 0x64 then output should be 0x00.
Following is the code that I have written, in which I am getting the Error that says:
Disallowed system call: SYS_socketcall
The code below may appear to be very inefficient and of very basic level, but that is for the sake of understanding.
int main(void)
{
int input = 0x00, output = 0x00, i = 0x00, j = 0x64, var = 0x00;
int in_arr[100], out_arr[100];
for(i = 0x00; i < 0x65; i++, j--)
{
out_arr[i] = j;
in_arr[i] = i;
}
printf("Input : ");
scanf("%x",&input);
for(var = 0x00; var < 0x64; var++)
{
if(input == in_arr[var])
{
output = out_arr[var];
printf("0x%x",output);
break;
}
}
return 0;
}
for(i = 0x00; i < 0x65; i++, j--) // Should be i < 0x64
Here i can have maximum of 101 elements (0 to 100) since 0x65 => 101 but declared to contain only 100 elements.
Make it as i < 0x64 and it should work.
To avoid such confusions, the best is to have macro as #define MAX_ELE 100 and use MAX_ELE in all the places of 0x64.
Related
I am using the STM32F0 using register level coding and am having problems with the CRC module.
Basically I cant get the results to agree with online calculators.
I've stripped it right back to as simple as possible.
If I just reset the CRC then read the Data Register out I get 0xFFFFFFFF which I would expect as that's the initial value.
Even if I write zero in though and get the result it does not agree with other tools.
The STM outputs 0xC704DD7B and the online tools give 0xF4DBDF21.
As far as I can see all the parameters are the same (I have not tried to hand calculate it!).
My bare bones code is (and I am reading the result in the debugger from the register)...
// Reset the CRC.
SET_BIT(CRC->CR, CRC_CR_RESET_Pos);
// Write 0.
CRC->DR, 0;
I am not really sure, but maybe this helps:
I once had the same problem and i tried to figure out how to get the "correct" CRC32. Unfortunately there is not "one" type how CRC32 could be calculated, but several of ways. See https://crccalc.com/
I allways leave the settings of the CRC peripheral on default:
Default Polynomial state -> Enable
Default Init Value State -> Enable
Enable Input Data Inversion Mode -> None
None Output Data Inversion Mode -> Disable
Except "Input Data Format", which I set to "Words".
When sending data to the peripheral, I revert the words "word-wise". The Result is reverted word-wise again. This leads to an CRC32 which can be verified as CRC32/MGPE2
I have a function, that tests, if If I have configured the CRC Peripheral correctly, so I get "the correct" CRC32/MPEG2:
uint8_t CRC32Test(void) {
// #brief test if CRC32 Module is configured correctly
// #params none, void
// #return u8 status: 1 = OK, 0 = NOK (not configured correctly)
// Test if CRC Module is configured correctly.
// If YES, these data must return the CRC32 0x07 D4 12 72 (Big Endian)
// or - 0x72 12 d4 07 (little Endian)
uint8_t retval = 0;
uint8_t testdata[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x00, 0x00};
uint32_t CRCdataU32[3] = {0,};
uint32_t* pCRCdata = (uint32_t*)testdata;
uint32_t dataSz = 3;
CRCdataU32[0] = __REV(*pCRCdata++);
CRCdataU32[1] = __REV(*pCRCdata++);
CRCdataU32[2] = __REV(*pCRCdata++);
uint32_t testCRC32 = HAL_CRC_Calculate(&hcrc, CRCdataU32, dataSz);
testCRC32 = __REV(testCRC32);
if(testCRC32 == 0x7212d407) retval = 1;
return(retval);
}
I verified this, using crccalc.com
This is most probably not the most elegant code, but it works for me. I use it for data transfer between the MCU and a PC over RS232/RS485. I don't care much which special CRC32 I use. I just need both to create the same results on the receiver and the sender. And I archieve that with that code.
First time using Modbus. I'm trying to send a request to read a temperature value from my slave device (ID is 0x01). I'm sending the command through UART and viewing command on my logic analyser.
Here is my code:
void temp_sensor()
{
//Transmit RTU to Inisitu probe to fetch Temperature and units
Flow_Control_SetHigh();
unsigned char TempRTU[]= {0x01, 0x03, 0x00, 0x2D, 0x00, 0x02, 0x54, 0x02};
int i;
int data_len = 8;
for (i = 0; i < data_len; i++) {
UART1_Write(TempRTU[i]);
}
}
0x01: Slave ID:
0x03: function code:
0x002D: Read temperature register:
0x0002: Read 2 registers:
0x5402: CRC Checksum:
The hex string is correct when viewing through UART on my analyser, however when setting the analyser to view Modbus, the string is not even close to being correct. I've attached a screenshot of my analyser while viewing the Modbus signal.
Has anyone got experience with Modbus and PIC microcontrollers?
I am currently writing a code to write on an LCD screen pixel by pixel. The code works fine, however the speed at which the code is processed is incredibly slow. The goal is simply to write number on the LCD screen so I am using the "switch" function with a "for loop" to read each of the bit I will activate. I am wondering if someone could tell me a way to speed up my code...
int* switch_library_number_1(int num, int octet)
{
switch(num)
{
case 0 : ;
int number_0 [] = {0x80, 0x08,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, ...};
int * pNumber_0 = &number_0[octet];
return pNumber_0;
break;
case 1 : ;
int number_1 [] = {0x80, 0x08,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, ...};
int * pNumber_1 = &number_1[octet];
return pNumber_1;
break;
}
Then it goes up to nine like that, I don't think you need to seem all the cases. Plus even if I deleted most of them, I have 522 bytes by number. The rest of the code goes as fallow :
int main(void)
{
ADC_Initialization();
SPI_Initialization();
int nombre_octet = 522;
int premier_nombre;
int deuxieme_nombre;
while(1)
{
GPIOA->BSRRL = CS;
for(int i = 0; i < nombre_octet; i++)
{
write_spi(*switch_library_number_1(0, i));
}
GPIOA -> BSRRH = CS;
for(int i = 0; i < 100; i++)
{
}
GPIOA->BSRRL = CS;
for(int i = 0; i < nombre_octet; i++)
{
write_spi(*switch_library_number_2(1, i));
}
GPIOA -> BSRRH = CS;
}
}
Finally, here is the write_SPI function, but due to it's simplicity, I don't think that it is the problem.
void write_spi(char data)
{
SPI1->DR = data;
while (!(SPI1->SR & SPI_I2S_FLAG_TXE));
while (!(SPI1->SR & SPI_I2S_FLAG_RXNE));
while (SPI1->SR & SPI_I2S_FLAG_BSY);
}
Thanks in advance!
I quite like the way you split your code into three snippets. I can suggest improvements for each of them:
switch_library_number_1():
This could be just a 2D array, number[][], or if number_0, number_1... are not of the same length, it could be an array of pointers to these. There would need to be checks for valid num and offset. This might be a minor speed improvement.
Your number_0... arrays are currently on stack, and read-write. Make them const, so they won't use RAM.
Currently you are returning a pointer to memory location on stack - this doesn't normally work, if it does it's by luck and accident. You should not access stack data when you're out of scope (function) where it's been defined. static const would make this safe, as it wouldn't be on stack anymore.
main loop:
It's a bit odd to call switch_library_number_1/2 on each loop iteration. You know your data will just be in array. This could probably be replaced by write_spi(number[0][i]); if number array is properly set up. This should get you some speed improvement, as it very much simplifies data fetching.
You appear to have a busy loop. That's a tricky practice (I bet 100 is a guess, and note that compiler could optimise this loop away). If possibly use some library provided delay function or a timer to get precise delays. Is this an actual requirement of SPI slave?
write_spi(char data):
char should be unsigned char here. chars might be signed or unsigned, so when you're using them as bytes (not actual string characters), you should specify signedness.
You seem to wait for every byte transmission to finish, which is safe, but a bit slow. Normally this can be rewritten into a faster alternative of wait_for_SPI_ready_for_TX; SPI_TX, where you only wait before sending next byte. Note that you will also need to wait for byte to be transmitted fully before pulling CS back high again. This could be a big speed improvement.
Some other things to consider:
What's the actual SPI clock? There may be huge speed improvements if clock is increased.
How did you measure this to be "slow"? Does it point to slow parts of code (what are those then? If not obvious from C, what are they assembled to?)
Have you got an oscilloscope/logic analyser to look at actual signals on wire? This may provide useful data.
I had a similar problem with STM32F207 Series Cortex-M3 controller, when I observed the TX line through Oscillator, I saw that CHIP_SELECT disable was taking too much time to set in, after all the data has sent.I figured out it is related to flag controls So ı play with the control flags a little bit, Here how it worked out just fine for me;
static void SPI_Send(uint16_t len,uint8_t* data)
{
uint16_t i;
for(i = 0;i<len;i++)
{
SPI_I2S_SendData(SPI1,*(data+i));
while(!(SPI1->SR & SPI_SR_TXE));
}
while(SPI1->SR & SPI_SR_BSY);
CHIP_SEL_DISABLE;
}
I believe it is slow because you are also checking the 'Receive Buffer Not Empty' where you don't need to.
I'm writing and reading values on a SD card in SPI mode. The frequency for writing and reading blocks of 512 bytes is set to 10Mhz. For writing a block it takes about 5ms and timing between blocks is about 10 ms.
Is any way to improve the speed of writing?
void sd_card_write_block(uint16 blockNumber, uint8* buffer)
{
uint16 blockLow = 0;
uint16 blockHigh = 0;
uint8 dummy = 0;
uint8 result = 0;
uint8 data_block_start_byte = 0;
uint8 write_command[SD_CMD_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8 dummy_buffer[DUMMY_BUFFER_LENGTH] = {0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF};
uint8 i = 0;
uint8 check_response[CHECK_RESPONSE_SIZE] = {0x00, 0x00, 0x00};
uint8 check_response1[CHECK_RESPONSE_SIZE] = {0x00, 0x00, 0x00};
uint8 r1 = 0;
uint16 retry = 0;
uint8 response1 = 0;
dummy = 0xFF;
//initialize the dummy buffer to keep MOSI pin High
for(i = 0; i < DUMMY_BUFFER_LENGTH; i++)
{
dummy_buffer[i] = 0xFF;
}
//set CS pin low
spi_select_slave( &spi_master_instance, &slave, true);
//send three clock cycles with MOSI HIGH (Ncs)
spi_write_buffer_wait( &spi_master_instance, dummy_buffer, NCS_LENGTH);
//block size was set in sd_init
blockLow = ((blockNumber & 0x003F) << 9);
blockHigh = ((blockNumber & 0xFFC0) >> 7);
//send SD CMD24(WRITE_SINGLE_BLOCK) to write the data to SD card
write_command[0] = 0x58;
//high block address bits, blockHigh HIGH and LOW
write_command[1] = (blockHigh >> 0x08);
write_command[2] = (blockHigh & 0xFF);
//low block address bits, blockLow HIGH and LOW
write_command[3] = (blockLow >> 0x08);
write_command[4] = (blockLow & 0xFF);
//checksum is no longer required but send 0xFF
write_command[5] = 0xFF;
spi_write_buffer_wait( &spi_master_instance, write_command, SD_CMD_SIZE);
spi_transceive_buffer_wait( &spi_master_instance, dummy_buffer, check_response, CHECK_RESPONSE_SIZE);
//send three clock cycles with MOSI High
spi_write_buffer_wait( &spi_master_instance, dummy_buffer, DUMMY_BUFFER_LENGTH);
//set bit 0 to 0 which indicates the beginning of the data block
data_block_start_byte = DATA_BLOCK_START_TOKEN;
spi_transceive_buffer_wait( &spi_master_instance, &data_block_start_byte, &result, SD_RESPONSE_SIZE);
/*takes so long because its similar to transreceivea and it discards the rx*/
spi_write_buffer_wait( &spi_master_instance, buffer, SD_BLOCK_LENGTH);
//read the microSD card response
spi_transceive_buffer_wait( &spi_master_instance, dummy_buffer, check_response1, CHECK_RESPONSE_SIZE);
do
{
// write dummy byte
spi_transceive_buffer_wait( &spi_master_instance, &dummy, &response1, SD_RESPONSE_SIZE);
r1 = response1;
// do retry counter
retry++;
if(retry > MAX_TIMEOUT)
{
spi_select_slave( &spi_master_instance, &slave, false);
break;
}
}
while(r1 == END_OF_BLOCK_RESPONSE);
//set the CS High
spi_select_slave( &spi_master_instance, &slave, false);
}
You can't, atleast not by much. The reason being that the SPI mode itself is the bottleneck. So, you can use a few tricks here and there to get that speed up, but you really won't be benifitting much from it. I'd advise you to use SDIO if you really need that much speed (It's not as complicated as you might think). If you're using an AVR, try the xmega lineup (Don't quote me on that, I'm not quite informed in the AVR, since I use ARMs), or switch to another lineup completely.
In order to get high write speeds, you need to:
- use a faster clock
- use a wider bus
- Write larger blocks (2k or 4k) at a time
In other words, you pretty much need to use the SDIO spec.
There's also ways to begin a large sequential transaction and not end it until all the data has been received (but maybe that's in the SDIO spec and not the SPI command interface). These sort of hints tells the controller that it can prepare a large section of the flash and not commit the write buffer until it's full.
And even then, you can end up with "very long" write cycles occasionally, so you cannot rely upon an SD card to stream high speed data off in real time without buffers on your side to handle the latency blips.
You should have posted your spi_write_buffer_wait code but consider something like this:
Rearange the stuff in your actual write function where you send the data. Think about it. You have to wait until the SD Card indicates you, that the byte was written successfully. Use this "useless" wait time to prepare the next data.
/* write a single block */
for ( i = MMC_BLOCKLEN; i > 0; i-- ) {
uint8_t data = *buffer;
buffer++;
wait_till_send_done();
xmit_byte ( data );
}
wait_till_send_done();
There you prepare the byte to send and also move the pointer one byte forward before you start waiting for the SD Card to indicate its successfull writing of the previous byte.
It is also a good idea to check the generated ASM code...
If you are about to write more than 512bytes of data, consider using multiblock writes (4k blocks for example at once).
Usually SD cards have internal block sizes of more than 512bytes. So to write 512 bytes, they have to internally read for example 4k, exchange your 512bytes and write everything back... so to write more data at once is always a good idea...
How do I change slave address of mlx90614 with bcm2835 library? I've tried following code...
int main()
{
// Buffer, where I store data which I'll send
unsigned char buf[6];
// bcm2835 i2c module intialisation code
bcm2835_init();
bcm2835_i2c_begin();
bcm2835_i2c_set_baudrate(25000);
bcm2835_i2c_setSlaveAddress(0x00);
// For debug purposes, I read what reason codes operations give.
bcm2835I2CReasonCodes why;
bcm2835_i2c_begin();
// function which reads and prints what value eeprom address 0x0e has.
// See below the main.
printf("Initial check\n");
check(); // this time it prints a factory default value 0x5a.
// To access eeprom, the command must start with 0x2X, where x determines the
// address, resulting 0x2e.
buf[0] = 0x2e;
// According to datasheet, I first have to clear the address before
// real write operation.
buf[1] = 0x00;
buf[2] = 0x00;
why = bcm2835_i2c_write(buf,3);
reason(why); // resolves and prints the reason code. This time it prints OK
// according to datasheet, eeprom needs 5ms to make a write operation,
// but I give it 2 seconds.
sleep(2);
// Then I check did the value in eeprom 0x0e change. IT DOESN'T!
printf("Check after clear\n");
check();
// Then I try to write a new address to the eeprom but since the clearing
// the register didn't work, this is very unlikely to work either.
buf[0] = 0x2e;
buf[1] = 0x4b;
buf[2] = 0x00;
why = bcm2835_i2c_write(buf,3);
reason(why);
sleep(2);
// The datasheet says that I have to reset the power supply and after that
// the device should respond to the new slave address.
// I do that by pluging off the jumper wires and reconnecting them
// after the program has finnished.
bcm2835_i2c_end();
return 0;
}
// The function I use to determine what the reason code was.
void reason(bcm2835I2CReasonCodes why)
{
printf("Reason is: ");
if(why == BCM2835_I2C_REASON_OK)
{
printf("OK");
}else if(why == BCM2835_I2C_REASON_ERROR_NACK){
printf("NACK");
}else if(why == BCM2835_I2C_REASON_ERROR_CLKT){
printf("Clock stretch");
}else if(why == BCM2835_I2C_REASON_ERROR_DATA ){
printf("Data error");
}else{
printf("Dunno lol");
}
printf("\n");
return;
}
// Here I read eeprom 0x2e.
void check()
{
unsigned char buf[6];
unsigned char reg = 0x2e;
bcm2835I2CReasonCodes why;
// better safe than sorry with the buffer :)
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
why = bcm2835_i2c_write (®, 1);
reason(why);
why = bcm2835_i2c_read_register_rs(®,&buf[0],3);
reason(why);
printf("Buffer values are: %x ; %x ; %x \n", buf[0], buf[1], buf[2]);
}
The output of the program is following:
Initial check
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK
Check after clear
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK
If I run i2cdetect -y 1 after that, the device doesn't appear in the table, but it responds to programs calling it from either 0x00 or 0x5a. After I've used such a program, the i2cdetect detects the device normally from address 0x5a.
So I guess the real question is, why I can't clear and rewrite the eeprom 0x0e?
The description of Mlx90614 SMBus communication can be found below. The most relevat page is IMO the page 19 which actually gives the pseudocode example of what I'm trying to do.
http://www.melexis.com/Assets/SMBus-communication-with-MLX90614-5207.aspx
Here's the datasheet for mlx90614
http://www.melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx
And here's the documentation for bcm2835
www.airspayce.com/mikem/bcm2835/group__i2c.html
You have to add an Error-Byte. Take a look at this website for an explanation: https://sf264.wordpress.com/2011/03/10/howto-mlx90614-und-pwm/
Calculating CRC-8 for 00002e4b00 gives 0xa3.
I used for calculating CRC-8 this website: http://smbus.org/faq/crc8Applet.htm
I haven't tested this, but I think this should work:
buf[0] = 0x2e;
buf[1] = 0x4b;
buf[2] = 0x00;
buf[3] = 0xa3;
why = bcm2835_i2c_write(buf,4);
Struggled with the exact same problem with my mlx90614s. Here is the write routine I used to solve it (Please note that the bcm2835-library was properly initalized before the call to the routine).
First I called the write routine with "correct" Slaveaddress, command=0x2E (EEPROMAccess | SMBusAddressReg) and data=0x0000 (for erase). The "correct" slave address can be 0x00 or the factory default 0x5a (or whatever is the chip's true address).
After erasing I used the same write routine but now with data=0x005b, to change from the factory default 0x5a to 0x5b, did a Power Off Reset (POR) and the device showed up with its new address (0x5b) using i2cdetect.
uint8_t memWriteI2C16(uint8_t SlaveAddress, uint8_t command, uint16_t data)
{
unsigned char arr[5];
uint8_t status;
//Prepare for CRC8 calc
arr[0] = SlaveAddress<<1; //NB! 7 bit address + a 0 write bit.
arr[1] = command; //Command byte in packet
arr[2] = *((uint8_t *)(&data)); //Extract data low byte
arr[3] = *((uint8_t *)(&data)+1);//Extract data high byte
arr[4] = crc8(&arr[0],4)&0xFF; //Calculate PEC by CRC8
bcm2835_i2c_setSlaveAddress(SlaveAddress);//Transmit address byte to I2C/SMBus
status = bcm2835_i2c_write (&arr[1], 4); //Transmit Command,DataL, DataH and PEC
bcm2835_delay(5); //Delay at least 5ms
return (status);
}
The CRC8 routine I used was:
// Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.
// A table-based algorithm would be faster, but for only a few bytes
// it isn't worth the code size.
// Ref: https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/master/firmware/lib/crc8.c
uint8_t crc8(const void *vptr, int len)
{
const uint8_t *data = vptr;
unsigned crc = 0;
int i, j;
for (j = len; j; j--, data++) {
crc ^= (*data << 8);
for(i = 8; i; i--) {
if (crc & 0x8000)
crc ^= (0x1070 << 3);
crc <<= 1;
}
}
return (uint8_t)(crc >> 8);
}
In addition: according to the data sheet for the the mlx90614, its default factory state after power up is PWM output. When hooking an mlx90614 in the factory PWM state to the I2C bus on the RPi2, the i2cdetect reports hundreds of I2C devices on the bus. Trying to access the mlx90614 by using the bcm2835-library fails. What is required is to force the mlx90614 out of its PWM-state by holding the SCL low for at least 2ms. Here is what I did:
uint8_t mlx90614SMBusInit()
{
//Hold SCL low for at leat 2ms in order to force the mlx90614 into SMBus-mode
//Ref Melix app note regarding SMBus comm chapter 6.1 and table 5.
uint8_t SCL1 = 3; //BCM2835 pin no 3 -RPi2 and RevB+. Use if i2cdetect -y 1
uint8_t SCL0 = 1; //BCM2835 pin no 1 -RPi2 and RevB+. Use if i2cdetect -y 0
uint8_t SCL;
SCL = SCL1;
bcm2835_gpio_fsel(SCL, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(SCL ,LOW);
bcm2835_delay( 3); //Delay >2 ms
bcm2835_gpio_write(SCL ,HIGH);
return (1);
}
However, this only hold until next power up. Hence it is required to write to the pwmctrl-register in mlx90614's eeprom (disable pwm output and force SDA to OpenDrain). I used the write routine as previously described with command=0x22 (i.e. EEPROMAccess | PWMCTRLAddressRegister) and after erasing the pwmctrl-register content, I wrote 0x0200 to it (the frst 3 nibbles was 020 in my devices...). Power Off Reset (POR) and the device started in SMBus-mode (no jamming of the I2C-bus). The mlx90614 is a tricky little component...
Also if you are using I2C-tools package from any linux distribution (in my case I'm using debian distro) you could change the address with the i2cset command (https://manpages.debian.org/buster/i2c-tools/i2cset.8.en.html), here is an example:
#Find your I2C bus in your linux with the command i2cdetect -l
#(in my case is the i2c-1)
i2cdetect -l
i2c-1 i2c bcm2835 I2C adapter I2C adapter
#Write the word 0x0000 to the address 0x2E and append the PEC check byte.
i2cset -y 1 0x5a 0x2E 0x0000 wp
#Write the new address as a word, to the address 0x2E and append the PEC
#check byte. In my case the new address is 0x005c
i2cset -y 1 0x5a 0x2E 0x005c wp
#Perform a power cycle of the Mlx90614 device
#Check the new address with the command i2cdetect -y 1
i2cdetect -y 1