TFT LCD ILI9486 and STM32 - stm32

I bought a TFT LCD Touch Screen Module ILI9486 from AliExpress.
I followed the instructions from this video Interface TFT display with STM32 in parallel mode
I found the TFT LCD Specification for this screen
Then, I am trying to get the Screen ID with this code:
uint16_t readID(void)
{
uint16_t ret;
uint32_t ID;
uint32_t ID4;
uint8_t msb;
ret = readReg(0,0); // forces a reset() if called before begin()
ret = readReg(1,0); // Soft reset
ID = readReg32(0x04); // Read ID 1er: dummy, 2nd ID manuf
ID4 = readReg32(0xD3); // Read ID4 1er: dummy, 2nd IC version
msb = ID4 >> 8;
if (msb == 0x93 || msb == 0x94 || msb == 0x98 || msb == 0x77 || msb == 0x16)
return ret; //0x9488, 9486, 9340, 9341, 7796
...
}
The values returns are:
ID = 0x85858585
ID4= 0x939b9b9b
I was expected 0x9486 inside ID or ID4.
I used a STM32F103C8
Is there something wrong with my code?

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;
}
}

Reading value from HC SR04 in LM016L

I'm programming STM32F103C6, and I'm using Keil Microvision with Proteus Professional 8, I have problem with reading value from HC SR04 Ultrasonic sensor in LCD(LM016L), the code is below
#define TRIG_PIN GPIO_PIN_9
#define TRIG_PORT GPIOA
#define ECHO_PIN GPIO_PIN_8
#define ECHO_PORT GPIOA
uint32_t pMillis;
uint32_t Value1 = 0;
uint32_t Value2 = 0;
uint16_t Distance = 0; // cm
uint16_t temp = 50;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim1);
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low
/* USER CODE END 2 */
lcd_init();
char buffer[24];
while (1)
{
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); // pull the TRIG pin HIGH
__HAL_TIM_SET_COUNTER(&htim1, 0);
while (__HAL_TIM_GET_COUNTER (&htim1) < 10); // wait for 10 us
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low
pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
// wait for the echo pin to go high
while (!(HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 10 > HAL_GetTick());
Value1 = __HAL_TIM_GET_COUNTER (&htim1);
pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
// wait for the echo pin to go low
while ((HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 50 > HAL_GetTick());
Value2 = __HAL_TIM_GET_COUNTER (&htim1);
Distance = (Value2-Value1)* 0.034/2;
sprintf(buffer, "%u\n", Distance);
lcd_puts(0,0, (int8_t*)(buffer));
HAL_Delay(50);
}
}
I think I got some value 10* -300, does anyone has idea how to read it or another code

How to interact with the Linux PCA953X.c driver? How does one utilize this 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.

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.