How to interact with the Linux PCA953X.c driver? How does one utilize this driver? - linux-device-driver

I have a PCA9535 GPIO Expander board connected through I²C to my Raspberry Pi. I am able to set the GPIO expander output pins (to high) using the i2cset command:
sudo i2cset 1 0x20 0x02 0xFF // 0x20 (gpio expander), and register 0x02
I came across a kernel driver for the PCA953X and loaded the kernel module gpio-pca953x.ko + modified the /boot/config.txt to include the dts overlay for the expander. When I run i2detect -y 1 (for i2c-1 bus), I can see "UU" at the 0x20 slave address, which corroborates that the driver is managing it.
If I wanted to do something comparable to what I did with the i2cset command programmatically, what API / interface should I use such that I make use of the PCA953X driver that's installed? As you can see from the code below, nothing there suggests that I would be utilizing PCA953X:
int file;
int adapter_nr = 1;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if (file < 0) {
exit(1);
}
// Writes 0xFF to register 0x02
uint8_t cmd[2] = {0x02, 0XFF};
struct i2c_msg msg = {
.addr = 0x20,
.flags = 0,
.len = sizeof(cmd)/sizeof(uint8_t),
.buf = cmd
};
struct i2c_rdwr_ioctl_data tx = {
.msgs = &msg,
.nmsgs = 1
};
ioctl(file, I2C_RDWR, &tx);
Is there a programming route that involves utilizing the PCA953X driver? What does having that module actually do?

Thanks to #TomV for pointing it out in the comments. The PCA953X driver provides a new character device in the form of "gpiochipN". In my case, it was gpiochip2. I had not noticed this extra gpiochip in /dev as I was not looking for it. Because this is a character device, I was able to use ioctl to interact with the lines:
int fd, ret;
fd = open("/dev/gpiochip2", O_RDONLY);
if (fd < 0) {
printf("Unable to open: %s", strerror(errno));
return;
}
struct gpiohandle_request req;
req.lineoffsets[0] = 0;
req.lineoffsets[1] = 1;
req.lineoffsets[2] = 2;
req.lineoffsets[3] = 3;
req.lineoffsets[4] = 4;
req.lineoffsets[5] = 5;
req.lineoffsets[6] = 6;
req.lineoffsets[7] = 7;
req.lines = 8;
req.flags = GPIOHANDLE_REQUEST_OUTPUT;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (-1 == ret) {
printf("Failed to get line handle:%s\n", strerror(ret));
close(fd);
return;
}
// Sets all 8 lines to high (equivalent to setting register 0x3 to 0b11111111 or 0xFF)
struct gpiohandle_data data;
data.values[0] = 1;
data.values[1] = 1;
data.values[2] = 1;
data.values[3] = 1;
data.values[4] = 1;
data.values[5] = 1;
data.values[6] = 1;
data.values[7] = 1;
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (-1 == ret) {
printf("Failed to set line value\n");
}
else {
close(req.fd);
}
close(fd);
The above code interacts with the gpiochip2 character device, which is made possible by having the PCA953X installed. Without it, I would have to read and write to the registers through the i2c bus as was posted in my original question.

Related

Using STM32 FMC HAL driver with parallel DAC

Im trying to generate sinusoidal signal with STM32f767 and DAC8412. DAC have 12 bit data bus and 2 bit address to select one of the four analog outputs. I've configuried FMC in CubeIDE for a SRAM memory with 16 bit data and 2 bit addres. I was able to create buffer with 4096 integer values of sin(). Then i've tried to write them to addres 0x60000000, but it only writes 4 values. After that, program goes to HardFault_Handler().
#define SRAM_BANK_ADDR ((uint32_t)0x60000000)
#define RESOLUTION_T ((uint32_t)0x1000)
#define RESOLUTION_Y ((uint32_t)0x1000)
uint32_t aTxBuffer[RESOLUTION_T];
uint32_t address = SRAM_BANK_ADDR;
Thats how i try to send data to DAC:
for (uint32_t i = 0; i<RESOLUTION_T; i++ )
{
*(__IO uint32_t*) address = aTxBuffer[i];
}
Thats how i fill buffer:
static void Fill_Buffer(uint32_t *pBuffer, uint32_t res_T, uint32_t res_Y)
{
uint32_t tmpIndex = 0;
double sinVal;
/* Put in global buffer different values */
for (tmpIndex = 0; tmpIndex < res_T; tmpIndex++ )
{
sinVal = round((sin(M_TWOPI*tmpIndex/res_T)+1)*res_Y/2);
pBuffer[tmpIndex] = sinVal;
}
}

Can Someone explain the use of `if (EP_DATA == 13)` in the code?

unsigned char i2c_read(void)
{
unsigned char i;
lsb = SDA;
for (i = 0; i <= 7; i++) {
EP_DATA = EP_DATA << 1;
lsb = SDA;
SCL = 1;
SCL = 0;
}
if (EP_DATA == 13) {
SDA = 1;
SCL = 1;
SCL = 0;
SDA = 0;
i2c_stop();
return EP_DATA;
}
SDA = 0;
SCL = 1;
SCL = 0;
SDA = 1;
return EP_DATA;
}
I found the code on GitHub for I²C protocol for communication with DS1307 written by Embetronicx and I was unable to understand the use of the if condition if (EP_DATA == 13)
if (EP_DATA == 13) {
SDA = 1;
SCL = 1;
SCL = 0;
SDA = 0;
i2c_stop();
return EP_DATA
}
So, please can someone explain the use to that.
I don't think that this can be real working code.
This is not least because there are no delays between the clock edges. Unless the microcontroller executes these instructions at I2C compatible speeds (1940s hardware maybe?) then this will not work.
The particular if block you refer to can never be entered. Because ED_DATA is left shifted 8 times in the preceding loop, it can never equal 13.
If I was going to guess the meaning of 13 then I might suggest that it was meant to be the numerical value of '\r' a carriage return, delimiting the end of a line of ASCII text. Really though only the author of the code can tell you for certain why they wrote this.

STM32F469 with AS4C4M32S SDRAM only able to write first byte

I have made a custom board that features a STM32F469NI MCU coupled with AS4C4M32S SDRAM.
Following are the SDRAM timing and register set information:
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 7;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 6;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FMC_Init 2 */
FMC_SDRAM_CommandTypeDef cmd = { 0 };
cmd.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
HAL_Delay(100);
cmd.CommandMode = FMC_SDRAM_CMD_PALL;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
cmd.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 8;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
cmd.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = (uint32_t) 0x230; //also tried 0x231
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
//Setup SDRAM Refresh Timer Register with the delay between refresh cycles of the SDRAM, this is SDRAM specific.
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 1404); //Tried 1386
This same code works when running on a STM32F469-I DISCOVERY Board but fails when It is run on my custom carrier board.
On my board, whenever I write to any SDRAM address, only the first byte is READ/WRITTEN to, for example when I write 0x00 to address 0xC0000000, the data at that address when monitored under debugging contains 0x48129100 i.e first 3 BYTES are random garbage and only the last byte is written correctly(0x00), and suppose If i write 0xAA to the same address the data there when monitored contains the value 0x481291AA.
This is true to every address that I read/write to/from the external SDRAM.
Only the last byte is written/read.
The custom board schematic and PCB layout is identical to the STM32F469-I DISCO board.
Only difference is the RAM chip which instead of being MICRON one is Alliance Memory's AS4C4M32S .
What can be the issue of such a problem?
Reading SDRAM always uses at the full bit width, but writing requires to set the four signals BL0, BL1, BL2 and BL3 to say which bytes to write.
It sounds like only BL0 is currently working. Perhaps BL1-3 are not physically connected (hardware) or perhaps you just forgot to configure them (software).
(If it turns out to be a hardware problem then someone with the appropriate permissions will want to move this question and answer to the electronics stack exchange)

MCP79411 RTC connection via i2c/TWI interface on Atmel SAMG55

I've made a project based on ATSAMG55J19 MCU, programmed with Atmel Studio and ASF 3
Now i'm trying to add an external RTC clock, because internal SAMg55 rtc does not have a backup battery.
The module will be used to read current time after power failure, then i'll use internal RTC, so i need only basic communication. No need to write specific data in EEPROM or setting alarms.
I have a MCP79411, connected via i2c, but there aren't any library suitable for this MCU that uses ASF TWI library.
There are many Arduino implementation, but they uses Wire.h library, and i can't port it.
I made an attempt porting this simple "driver": https://www.ccsinfo.com/forum/viewtopic.php?t=54105
Here is some code
static void i2c_start(void){
static twi_options_t ext3_twi_options;
flexcom_enable(FLEXCOM4);
flexcom_set_opmode(FLEXCOM4, FLEXCOM_TWI);
ext3_twi_options.master_clk = sysclk_get_cpu_hz();
ext3_twi_options.speed = 100000;
ext3_twi_options.smbus = 0;
twi_master_init(TWI4, &ext3_twi_options);
}
// Init Real Time Clock
void rtc_Init(void)
{
uint8_t seconds = 0;
i2c_start();
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, ADDR_RTCC_READ); // RD from RTC
seconds = bcd2bin(i2c_read(0)); // Read current "seconds" in rtc
//i2c_stop();
//seconds &= 0x7F;
seconds |= 0x80; //set to 1 bit 7 of seconds(ST) enabling oscillator
delay_us(3);
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, bin2bcd(seconds) | 0x80); // Start oscillator with current "seconds value
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, 0x07); // Control Register
twi_write_byte(TWI4, 0x80); // Disable squarewave output pin
//i2c_stop();
}
Then i tried rtc_set_date_time(uint8_t day, uint8_t mth, uint8_t year, uint8_t dow, uint8_t hr, uint8_t min, uint8_t sec)
and
void rtc_get_time(uint8_t &hr, uint8_t &min, uint8_t &sec)
{
twi_write_byte(TWI4, ADDR_RTCC_WRITE);
twi_write_byte(TWI4, 0x00);
twi_write_byte(TWI4, ADDR_RTCC_READ);
sec = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f b01111111
min = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f
hr = bcd2bin(twi_read_byte(TWI4) & 0x3f); //0x3f b00111111
//i2c_stop();
}
But i always get "0" bytes.
i could not understand the correct way to open communication and read bytes from i2c.
The only reference i found is http://asf.atmel.com/docs/latest/sam.drivers.twi.twi_eeprom_example.samg53_xplained_pro/html/index.html but it seems to be a very different type of communication.
What is the correct way to send and receive that bytes via i2c?
I managed to get and set data. I post a draft of library:
#include "asf.h"
#include "conf_board_3in4out.h"
#include "external_rtc.h"
#include <time.h>
//#include <time_utils.h>
twi_packet_t packet_tx, packet_rx;
// helper functions to manipulate BCD and binary to integers
static int bcd2dec(char r_char)
{
MSN = (r_char & 0xF0)/16;
LSN = r_char & 0x0F;
return(10*MSN + LSN);
}
static char msn(char tim)
{
return (tim & 0xF0)/16;
}
static char lsn(char tim)
{
return (tim & 0x0F);
}
#define RTC_ADDR 0x6F // 7 bits
char config_t[10];
char config_2[8];
char tim_read[8];
#define ADDR_SEC 0x00 // address of SECONDS register
#define ADDR_MIN 0x01 // address of MINUTES register
#define ADDR_HOUR 0x02 // address of HOURS register
#define ADDR_DAY 0x03 // address of DAY OF WEEK register
#define ADDR_STAT 0x03 // address of STATUS register
#define ADDR_DATE 0x04 // address of DATE register
#define ADDR_MNTH 0x05 // address of MONTH register
#define ADDR_YEAR 0x06 // address of YEAR register
#define ADDR_CTRL 0x07 // address of CONTROL register
#define ADDR_CAL 0x08 // address of CALIB register
#define ADDR_ULID 0x09 // address of UNLOCK ID register
#define ADDR_SAVtoBAT_MIN 0x18 // address of T_SAVER MIN(VDD->BAT)
#define ADDR_SAVtoBAT_HR 0x19 // address of T_SAVER HR (VDD->BAT)
#define ADDR_SAVtoBAT_DAT 0x1a // address of T_SAVER DAT(VDD->BAT)
#define ADDR_SAVtoBAT_MTH 0x1b // address of T_SAVER MTH(VDD->BAT)
#define START_32KHZ 0x80 // start crystal: ST = b7 (ADDR_SEC)
#define OSCON 0x20 // state of the oscillator(running or not)
#define VBATEN 0x08 // enable battery for back-up
static uint8_t bin2bcd(uint8_t binary_value)
{
uint8_t temp;
uint8_t retval;
temp = binary_value;
retval = 0;
if(temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else
{
retval += temp;
//break;
}
return(retval);
}
static uint8_t bcd2bin(uint8_t bcd_value)
{
uint8_t temp;
temp = bcd_value;
temp >>= 1;
temp &= 0x78;
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
static void setConfig(void){
config_2[0] = tim_read[0] | START_32KHZ; // bitwise OR sets Start osc bit = 1
config_2[1] = tim_read[1];
config_2[2] = tim_read[2];
//0x03h – Contains the BCD day. The range is 1-7.
//Bit 3 is the VBATEN bit. If this bit is set, the
//internal circuitry is connected to the VBAT pin
//when VCC fails. If this bit is ‘0’ then the VBAT pin is
//disconnected and the only current drain on the
//external battery is the VBAT pin leakage.
config_2[3] = tim_read[3] | VBATEN;
config_2[4] = tim_read[4];
config_2[5] = tim_read[5];
config_2[6] = tim_read[6];
config_2[7] = 0x00; // control b3 - extosc = 0
}
static void initialize(void){
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
// read stored time data
config_t[0] = 0x00; //reset pointer reg to '00'
// Set up config to read the time and set the control bits
//i2c.write(addr, config_t, 1); // write address 00
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_t;
packet_tx.length = 1;
twi_master_write(TWI4, &packet_tx);
delay_ms(250);
//
//i2c.read(addr, tim_read, 7); //read time ss mm hh from r1, r2, r3
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = tim_read;
packet_rx.length = sizeof(tim_read);
twi_master_read(TWI4, &packet_rx);
delay_ms(250);
setConfig(); //puts RTCC data into config array from tim_read array
// write the config data
//i2c.write(addr, config_t, 9); // write the config data back to the RTCC module
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void write_time(void){
// re-calculate mins
mins = 8; //ORE 10:08
mins = mins%60;
ch_mins = 16*(mins/10) + mins%10;
MSN = msn(ch_mins);
LSN = lsn(ch_mins);
tim_read[1] = ch_mins;
inc_mins = 0;
//write the data back to RTCC
setConfig();
//i2c.write(addr, config_t, 9);
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
//Display and set hours
//hrs = bcd2dec(tim_read[2]);
// re-calculate hrs
hrs = 10; //ORE 10:08
hrs = hrs%24;
ch_hrs = 16*(hrs/10) + hrs%10;
MSN = msn(ch_hrs);
LSN = lsn(ch_hrs);
tim_read[2] = ch_hrs;
inc_hr = 0;
//write the data back to RTCC
setConfig();
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void read_time(void){
//config_t[0] = 0x00; //reset pointer reg to '00'
//// First Get the time
////i2c.write(addr, config_t, 1); // write address 00
//packet_tx.chip = RTC_ADDR;
//packet_tx.addr[0] = 0; // RTCSEC
//packet_tx.addr_length = 1;
//packet_tx.buffer = config_t;
//packet_tx.length = 1;
//
//twi_master_write(TWI4, &packet_tx);
delay_ms(250);
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
/* Configure the data packet to be received */
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = buf;
packet_rx.length = sizeof(buf);
twi_master_read(TWI4, &packet_rx);
for(uint8_t i = 0; i < sizeof(buf); i++){
tim_read[i] = buf[i];
}
}
void example_print_time(void){
//initialize();
delay_ms(1000);
//write_time(); //commented to see if time is permanent
delay_ms(1000);
read_time();
while(1){
printf("Reading time\n");
printf("%d:%d:%d\n", bcd2dec(tim_read[2]), bcd2dec(tim_read[1]), bcd2dec(tim_read[0] ^ START_32KHZ));
delay_ms(1000);
read_time();
}
}

stm32f4 write/read 16bits into two 8-bit gpio port

I try to write 16 bit data into two selected 8-bits gpio ports . I must split data for
LSB and MSB :
void LCD_write_command(uint16_t cmd) {
GPIOD->ODR = cmd & 0x00ff; //lsb
GPIOA->ODR = (GPIOA->ODR & 0x00ff) | (cmd >> 8); //msb
}
and read data :
uint16_t LCD_read_data(void) {
(here is instruct gpio as input)
volatile uint16_t data = 0;
data = (uint16_t)GPIOD->IDR & 0x00ff; //lsb
data |= (uint16_t)GPIOA->IDR << 8 ; // msb
(here is instruct gpio as output)
return data;
}
When i use one 16bit gpio to write and read everything is fine:
void LCD_write_command(uint16_t cmd) {
GPIOD->ODR = cmd & 0xffff;
}
uint16_t LCD_read_data(void) {
volatile uint16_t data = 0;
data = (uint16_t)GPIOD->IDR & 0xffff;
return data;
}
I relay dont know what im missing.
wtite_bits(uint16_t cmd)
{
uint32_t data = GPIOA -> ODR;
data &= ~(0x1fff);
data |= cmd & 0x1fff;
GPIOA -> ODR = data;
data = GPIOB -> ODR;
data &= ~(0x0007);
data |= (cmd & 0x8fff) >> 13;
GPIOB -> ODR = data;
}
preserve other bits in the register
You need to learn a bit more about bitwise operations.
Writing
void LCD_write_command(uint16_t cmd) {
uint32_t tmp = GPIOD->ODR;
tmp &= ~(0xff);
tmp |= (cmd & 0x00ff);
GPIOD->ODR = tmp; //lsb
tmp = GPIOA->ODR;
tmp &= ~(0xff);
tmp |= (cmd >> 8);
GPIOA->ODR = tmp; //msb
}
or
void LCD_write_command(uint16_t cmd) {
*(volatile uint8_t *)&GPIOD->ODR = cmd & 0xff;
*(volatile uint8_t *)&GPIOA->ODR = cmd >> 8; //msb
}
forcing the compiler to use 8 bit store instructions.
Before using non word access to the registers check in the RM if your micro allows it:
Non of yours sugested code works for me , below is original source code of handling with LCD:
void LCD_write_command(uint16_t cmd) {
GPIOB->BRR = LCD_CS; // LCD_CS low (chip select pull)
GPIOB->BRR = LCD_RS; // LCD_RS low (register select = instruction)
//GPIOA->ODR = cmd; // put cmd to PortA (full length)
// put cmd [0..12] bits to PortA (actual LCD_DB00..LCD_DB12)
// put cmd [13..15] bits to PortB (actual LCD_DB13..LCD_DB15)
GPIOA->ODR = cmd & 0x1fff;
GPIOB->ODR = (GPIOB->ODR & 0xfff8) | (cmd >> 13);
GPIOB->BRR = LCD_WR; // pull LCD_WR to low (write strobe start)
// Write strobe 66ns long by datasheet. GPIO speed on STM32F103 at 72MHz slower -> delay is unnecessary
// asm volatile ("nop");
GPIOB->BSRR = LCD_WR; // pull LCD_WR to high (write strobe end)
GPIOB->BSRR = LCD_CS; // LCD_CS high (chip select release)
}
I've checked RM and i can operate on 8-bit ,half word and word.
I had similar problem: need to write some bits to port at same time.
My output bits are on port B: 0,1,2,4,5,6,7,8,9 (see missing bit 3).
It is possible to read whole port (ODR), and/or bits and write it back.
Faster version is to set all bits:
GPIOB ->BSRR =0b00000000000000000000001111111011;
and then clear only ones needed:
GPIOB ->BRR =some_bits_to_clear;
It was like some custom 74LS154 chip.
Or even try to clear and set bits at same time using:
GPIOB ->BSRR =bits_to clear<<16 | bits_to_set;
Depending on situation, there are many ways to optimize code.