Skip to main content
YTimm.1
Associate II
November 11, 2020
Question

directly read register status instead of HAL_GPIO_Readpin();

  • November 11, 2020
  • 4 replies
  • 3991 views

i am trying to bit bang 14 bits grey code. my output is not steady

uint16_t readbits(uint16_t dataPin, uint16_t clockPin) {

 uint16_t value =0;

  for (int i = 0; i <14; ++i) {

  value <<= 1;

  HAL_GPIO_WritePin(GPIOC, clockPin, GPIO_PIN_RESET);

  delay_us(20);

  HAL_GPIO_WritePin(GPIOC, clockPin, GPIO_PIN_SET);

  delay_us(20);

    value |= HAL_GPIO_ReadPin(GPIOC, dataPin);

  }

  return value;

 }

i think HAL_GPIO_ReadPin is not fast enough

in the while loop ()

DATA1 = readbits(DATA1_L_Pin, CLK_SSI_PORT_Pin);

how can change HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2); for example to read register status?

This topic has been closed for replies.

4 replies

Danish1
Lead III
November 11, 2020

It looks as though you are trying to bit-bang over an interface similar to SPI, where the stm32 is in control of the data clocking.

As such, making it faster will only improve things if the data are changing too rapidly for your slow read-process, but will be stable over a faster read-process.

I don't see mention of what device is generating this grey code. Telling us might help us.

I see some issues with your code.

  • The first is that you are clocking in 14 bits. 14 is unusual; I would normally expect a multiple of 8 bits, even if not all 8 bits are used.
  • Do you have a "chip select" line as well, that you pull low prior to reading the bits, then high when done? If not, how do you ensure that what the source device thinks is the first bit, is actually the first bit that your stm32 sees?
  • One other thing to check is what clock-edge does the data actually change on. If the data changes on the falling edge, your code is fine.

Hope this helps,

Danish

YTimm.1
YTimm.1Author
Associate II
November 11, 2020

thanks for the fast answer !, i will explain something. The result of uint16 is because i think this is the best way to save the resulting data. The data need to change on the rising edge.

the uC is the master of the clock, according to my logic analyser it is around 24KHz. SSI needs to have a time-out after the 14-bit word. it needs to change to high after it.

the sensor gives data on the falling edge. I use 4 of them so i need to make a function after to read 4 of this words with one clock but that is a problem for later, first to fix one of them.

some backgrounth: i try to read SSI absolute encoder. First MSB and last LSB in total a word of 14 bits: resolution 16384. The sensor is in grey code and no chip select option. i read on internet something like this: (this is for 25 bit encoder)

#define EncDataBit PIND2 // PD2 pin 2 of port D input

#define EncClock PORTD3 // PD3 pin 3 of port D output

int count;

uint32_t encData = 0;

uint32_t binData = 0;

encData = 0;

PORTD &= ~(1 << EncClock); // set output PD3 of PORTD low (0) Encoder Clock

_delay_us(10);

for(count = 0; count < 25; count++) {

PORTD |= (1 << EncClock); // set output PD3 of PORTD high (1) Encoder Clock

_delay_us(5);

if(bit_is_clear(PIND,EncDataBit)) {

encData = encData << 1;

} else {

encData = encData << 1;

encData = encData + 1;

}

_delay_us(5);

PORTD &= ~(1 << EncClock); // set output PD3 of PORTD low (0) Encoder Clock

_delay_us(10);

}

i know what is happening here but get a lot of errors to convert this code in STM32CubeIDE. maybe is this more information about the problem

some tips would be nice, thanks in advance

YTimm.1
YTimm.1Author
Associate II
November 11, 2020

code what i got now:

#define SSI_CLK_BIT CLK_SSI_PORT_Pin

#define SSI_CLK_PORT CLK_SSI_PORT_GPIO_Port

#define SSI_DTA_BIT DATA1_L_Pin

#define SSI_DTA_PORT GPIOC

uint16_t DATA1_ReadSSI (void)

 {

  uint8_t bit_count_data;

  uint8_t u8portdata1;

  uint16_t data1;

  for (bit_count_data=0; bit_count_data<14; bit_count_data++)

  {

  // falling edge on clock port

  SSI_CLK_PORT &= ~(1 << SSI_CLK_BIT); lvalue needed on left hand operand of assignement

  // left-shift the current result

  data1 = (data1 << 1);

  // read the port data

  u8portdata1 = SSI_DTA_PORT; assignement makes integer from pointer whithout a cast

  // rising edge on clock port, data changes

  SSI_CLK_PORT |= (1 << SSI_CLK_BIT); lvalue needed on left hand operand of assignement

  // evaluate the port data (port set or clear)

  if ( (u8portdata1 & (1 << SSI_DTA_BIT)) != 0)

  {

  // bit is set, set LSB of result

  data1 = data1 | 0x01;

  } // if

  } // for

  return data1;

 }

Ozone
Principal
November 11, 2020

> i am trying to bit bang 14 bits grey code. my output is not steady

I had done similar thing in one of my former companies, no vendor supported biphase (differential manchester code) out-of-the-box.

And of course you can hit direct on the GPIO in/out registers.

Use the appropriate GPIO struct, which is an overlay of the actual peripheral. The register names depend a bit on the derivate.

STMs also have BSR and BRR register (bit set and bit reset, or combined to BSRR), which are very useful in your case.

I would avoid using HAL delay() function, and use a timer interrupt instead.

YTimm.1
YTimm.1Author
Associate II
November 11, 2020

yes that is a great option. Can i generate a kind of clock with it? and yes i agree to avoid HAL_ functions, this is blocking in code>? on internet i cant find a good example for doing this.

the delay_us();

void delay_init ()

{

HAL_TIM_Base_Start(&htim10); // change this according to ur setup

}

void delay_us (uint16_t delay)

{

__HAL_TIM_SET_COUNTER(&htim10, 0); // reset the counter

while ((__HAL_TIM_GET_COUNTER(&htim10))<delay); // wait for the delay to complete

}

thanks for your reply

Ozone
Principal
November 11, 2020

I avoid Cube/HAL, and haven't used it in any of my projects.

I would setup a simple timer (up or down) with the bit time, and handle the bit set/reset stuff in the interrupt handler.

After the last bit, disable the interrupt from within the handler.

Never directly dealt with gray code.

But we used the timer for our biphase reception as well, without interrupts.

We just evaluated the timing of the phase changes (timer values), and checked if they were in limits.

This was about the DALI interface standard, that requires timing tolerance checks.

TDK
November 11, 2020

Your code would look something like this instead:

for (int i = 0; i <14; ++i) {
 value <<= 1;
 // HAL_GPIO_WritePin(GPIOC, clockPin, GPIO_PIN_RESET);
 GPIOC->BSRR = clockPin << 16;
 delay_us(20);
 // HAL_GPIO_WritePin(GPIOC, clockPin, GPIO_PIN_SET);
 GPIOC->BSRR = clockPin;
 delay_us(20);
 // value |= HAL_GPIO_ReadPin(GPIOC, dataPin);
 if (GPIOC->IDR & dataPin) {
 value |= 1;
 }
}

"If you feel a post has answered your question, please click ""Accept as Solution""."
YTimm.1
YTimm.1Author
Associate II
November 12, 2020

it worked ! i missed one falling edge, because ssi word starts when de clock goes low and then up. Than i need the 14 bit data burst.

with your answer and Ozone i made the following function: it reads 4 pins at the same clock

void CLK_and_reading(uint16_t clockPin) {

 DATA1 =0; DATA2 =0; DATA3 =0; DATA4 =0;

 GPIOC->BSRR = clockPin << 16; delay_us(4);

 GPIOC->BSRR = clockPin; delay_us(4);

 for (int i = 0; i <14; ++i) {

  DATA1 <<= 1; DATA2 <<= 1; DATA3 <<= 1; DATA4 <<= 1;

  GPIOC->BSRR = clockPin << 16; delay_us(4);

  GPIOC->BSRR = clockPin;

  if (GPIOC->IDR & DATA1_L_Pin) { DATA1 |= 1; }

  if (GPIOC->IDR & DATA2_L_Pin) { DATA2 |= 1; }

  if (GPIOC->IDR & DATA3_L_Pin) { DATA3 |= 1; }

  if (GPIOC->IDR & DATA4_L_Pin) { DATA4 |= 1; }

    delay_us(1);

  }

 }

YTimm.1
YTimm.1Author
Associate II
November 11, 2020

with the line: GPIOC->BSRR = clockPin << 16; this is for specific GPIO_PIN_1 ?

TDK
November 11, 2020
Read about the BSRR register in the reference manual. The upper 16 bits reset the output (low), the 16 lower bits set the output (high).
clockPin should be some combination of GPIO_PIN_x values.
"If you feel a post has answered your question, please click ""Accept as Solution""."