I am trying to use an lcd screen on my stm32F3discovery.
The screen is made of an lcd 16 characters on 2 lines and an I2C module.
Here is the link of the product:
https://www.aliexpress.com/item/32763867041.html?spm=a2g0s.9042311.0.0.27424c4dsV7dLS
On the back of the screen I can see written: QAPASS 1602A
On the chip of the I2C module I can see written: PCF8574T
Here is the datasheet of the chip:
https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
I tried to follow this tutoriel (the closest from what I am trying to do):
https://www.youtube.com/watch?v=1COFk1M2tak
I use the HAL library, the main function to send data is "HAL_I2C_Master_Transmit".
Here is the description of the function in "HAL_I2C_Master_Transmit":
#brief Transmits in master mode an amount of data in blocking mode.
#param hi2c Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
#param DevAddress Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
#param pData Pointer to data buffer
#param Size Amount of data to be sent
#param Timeout Timeout duration
#retval HAL status
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
//I initialise a buffer
//I use a for loop to find the address of my slave: 0x3D (even tho on the //datasheet it's 0x3F, looks like A1 is bridged :O )
//I use the HAL_I2C_Master_Transmit function
//I move the address one bit to the left
//I reuse the HAL_I2C_Master_Transmit
//Nothing happens on the screen
//Here is my code (I tried to remove the useless comments):
#include "main.h"
I2C_HandleTypeDef hi2c1; // Init generated bu CubeMX
SPI_HandleTypeDef hspi1; // Init generated bu CubeMX
PCD_HandleTypeDef hpcd_USB_FS; // Init generated bu CubeMX
uint16_t adresseLCD; // the variable I put the slave address on
uint8_t buffer[]="123"; // The buffer I wanna see on the screen
void SystemClock_Config(void); // Generated by CubeMX
static void MX_GPIO_Init(void); // Generated by CubeMX
static void MX_I2C1_Init(void); // Generated by CubeMX
static void MX_SPI1_Init(void); // Generated by CubeMX
static void MX_USB_PCD_Init(void); // Generated by CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
adresseLCD=0x3D;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
adresseLCD=adresseLCD*2; // Yes I could have used "adresseLCD<<1" but I
//am not used to that
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
while(1)
{
}
}
I expected something to show on the screen (even random values) but nothing appears (it lights up though).
I get no error (only warnings because I use "1" instead of "Pin_ON" when I WritePIn)
You expect it wrong.
First of allб the I2C module, which is soldered on back, it is just simple serial-to parallel convertor. When you write data on I2C bus, it sets its 8 outputs according to 8 received bits in data bytes. When you read data bytes, it switches into input mode and read logical levels on 8 pins and transmits it over serial wire. More detailed description you can read in the datasheet on PCF8574 you have provided.
I.e. this part does not perform any "magic" which will take characters on input and output them on display. That means the outputting of random data is futile.
You need to know two things more:
how this module is connected to the display driver
what kind of display driver is used and how to utilize it.
Answer to the first question may be found in the Internet:
(taken from here)
You can see the display is connected in 4-bit mode, outputs P4-P7 (i.e. what you transmit in four most significant bits of data bytes) are connected to data lines of the display, while outputs P0-P2 are connected to control lines RS, R/W, EH and P3 is used to control the backlight.
Knowing this we came to the second question. I can only suggest, but more likely your display module have Hitachi HD44780 IC on it. In the datasheet on it you can found the information what data should be output on the control lines.
You can find pin description at page 8:
bit RS selects whatever it be a command (0) or a data (1)
bit R/W chooses write opertion (0) or read (1)
and bit E is actually a strobe. On the falling edge, i.e. when it changes from 1 to 0, the display driver reads data from data lines. That means to pass 4 bits of data you should perform 2 write operations: first one with bit 2 is set high, second with all other bits are the same, but the bit 2 is zero.
Now you can read list of instructions at page 25 of datasheet. And initialization sequence for the 4-bit mode at page 46 (Figure 24). Note for each line of bits there you actually send 2 data bytes: with bit 2 high and then with bit 2 low.
Note, in 4-bit mode all commands and data consist of two write phases: first - top half, then bottom half of a byte. Each phase is 2 data writes to I2C module, with E bit high and low, i.e. you'll need to send 4 bytes to output 1 byte of data.
So basically in order to transmit data from you STM32 to the LCD display driver (HD44780) you need to emulate the latter's interface signalling via the I2C interface chip (PCF8574).
In other words, MCU will send I2C commands that will toggle the I2C "backpack" chip such that it should emulate the right signaling for the LCD driver.
This happens somehow easy whne you are using the HAL_I2C_Master_*() methods. In the buffer array you specify the state of the pins on the LCD as you want them and in the order [0], [1], [2]... etc. For example, let's say that we have DB[7:4] connected to the upper 4 bits of the PCF I2C expander. We can setup the following:
buffer[0] = 0xD0 ; // 0b11010000;
buffer[1] = 0xA0 ; // 0b10100000;
buffer[2] = 0xF0 ; // 0b11110000;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 3, 10); // Note 3 bytes are sent
Once the buffer is prepared, the HAL_I2C_Master_Transmit() call will send the tree bytes consecutively, thus toggling the DB pins as you have mentioned:
DB7: 1 -> 1 -> 1
DB6: 1 -> 0 -> 1
DB5: 0 -> 1 -> 1
DB4: 1 -> 0 -> 1
The same can be applied to all 8 pins on the PCF chip. It is also worth noticing that this sequential IO update also creates a bit less of I2C communication overhead as it only addresses the I2C slave chip once :) .
By the way, I am currently working on a LCD over I2C Library for STM32F1xx (probably it will fit other STM32F series). You can check it out on github (still WIP):
https://github.com/meteowrite/stm32_i2cLcd
Cheers
Related
I am stuck on the following problem. Consider this code:
int main(void)
{
SysTickInit();
USART_GPIOInits();
USART_Inits();
char data[] = "hello\n";
for(uint8_t i=0; i<10; i++)
{
HAL_UART_Transmit(&Usart1, (uint8_t*)data, strlen(data), 1000);
}
while(1){}
}
I try to send hello\n to Hercules in 10 times, but Hercules did not receive what i sent
this is what Hercules got , it had þ every the first time I reset the MCU. But , when I used Debugger mode, it did not get any error.
below is transmit function
below is Init function
but want to communicate with fingerprint , but because of this wrong i cant communicate
Change the order of initialization.
From
USART_GPIOInits();
USART_Inits();
to
USART_Inits();
USART_GPIOInits();
UART's default line state is logic high, logic low (start bit) launches a new transfer.
When GPIO is inialized first, with the corresponding peripheral module disabled, most likely you'll gen a logic low level on the TX pin, because there is no one to set it to a logic high (since UART is still disabled). When UART is initialized, it sets the TX line to a logic high (stop bit), and the terminal appication receives it as a broken byte.
Check your schematics
During and after reset CPU outputs are tri-stated. Most likely they'll stay at zero level until the configuration code will do it's job, leading to the same issue - receiving a garbage byte after the reset.
To prevent it, voltage levels on the interface pins must be defined during reset phase with an external pull-up resistor, like 10kOm, from TX and RX pins to VCC.
Please help! I am using FSMC to connect a STM32F407 MCU with AD7606 to sample voltage value. MCU would send sampled values to PC using USB HS port after 1024 conversions. But when I inspect the values from PC, I found that readings from channel 0 occasionally contains data from other channels. For example, if connect channel 0 to 5v, connect channel 8 to 3.3v, connect other channels to ground. Then the printed value from channel 0 would contain 5v, 0v, 3.3v. The basic setup is as follows:
A 200KHZ PWM single is generated by TIM10 to act as CONVST signal for AD7606.
7606 will then issue a BUSY signal which I used as an external interrupt source.
In the Interrupt handler, An DMA request would be issued to read 8 16bit data
from FSMC address space to memory space. TIM10 PWM would be stopped if 1024
conversions has been done.
In the DMA XFER_CPLT call back, if 1024 conversions has been done, the converted
data would be sent out by USB HS port, and TIM10 PWM would be enabled again.
Some code blocks:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_7)
{
// DMA data from FSMC to memory
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, 0x6C000000, (uint32_t)(adc_data + adc_data_idx) , 8);
adc_data_idx += 8;
if (adc_data_idx >= ADC_DATA_SIZE)
HAL_TIM_PWM_Stop(&htim10, TIM_CHANNEL_1);
}
}
void dma_done(DMA_HandleTypeDef *_hdma)
{
int i;
int ret;
// adc_data[adc_data_idx] would always contain data from
// channel 1, led1 wouldn't light if every thing is fine.
if (adc_data[adc_data_idx] < 0x7f00 )
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
if (adc_data_idx >= ADC_DATA_SIZE)
{
if(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED)
{
// if I don't call CDC_Transmit_HS, everything is fine.
ret = CDC_Transmit_HS((uint8_t *)(adc_data), ADC_DATA_SIZE * 2 );
if (ret != USBD_OK)
{
HAL_GPIO_WritePin(led1_GPIO_Port, led2_Pin, GPIO_PIN_SET);
}
}
adc_data_idx = 0;
HAL_TIM_PWM_Start(&htim10, TIM_CHANNEL_1);
}
}
It seems that a single USB transaction would take longer than 5us(one conversion time), so I stopped PWM signal to stop conversion...
If I only send the second half of the data buffer, there is no data mixture. It's very strange.
According to your description, I think the processing is correct, and the problem is at the CDC_Transmit_HS(); I have met the problem on the CDC_Transmit_FS(), which can't transmit more than 64 bytes data for original code, and need to modify some code, otherwise the some error occurs. Did you check the number of received data is correct?
Reference:
I can't receive more than 64 bytes on custom USB CDC class based STM32 device
I'm not sure your ADC_DATA_SIZE size; if it's larger than 64 bytes, maybe you can modify to smaller than 64 bytes and try again and check whether or not the data is correct. I am not sure if it is affected by this problem, but I think you can give it a try.
On the other hand, it may also be necessary to GND the ADC IN pins not used by AD7606 to avoid interference between channels.
Or you can try other communication (I2C, SPI, UART...etc) to send the data.
If there is no problem with other communication methods, there is a high chance that it is a problem with CDC_Transmit_HS(). If there are problems with other transmission methods, you may have to check whether there is a conflict between the ADC conversion time or the transmission time.
I'm using an efm32lg230f256 microcontroller and in its code there is a line which uses USART_Rx and it returns:
1 2 3 4
but when I look inside of it I can't see how it retuns 1 2 3 4. I tried to look in the data sheet but there are no such names.
What is the logic in this function? And why does it do this?
c = USART_Rx(uart);
uint8_t USART_Rx(USART_TypeDef *usart)
{
while (!(usart->STATUS & USART_STATUS_RXDATAV))
;
return (uint8_t)usart->RXDATA;
}
"1 2 3 4" is simply the device password
This is a HAL (Hardware Abstraction Layer).
It is probably defined something like this:
typedef struct {
//other uart registers
uint8_t STATUS
//other uart registers
uint8_t RXDATA
} USART_TypeDef;
#define USART_STATUS_RXDATAV 0b00001000 // or equivalent hex value
This struct is a way of interfacing with the memory mapped to the USART peripheral. As such the USART_TypeDef *usart pointer points to a piece of memory which is the location of an USART. When it reads usart->STATUS, it reads the data in the location pointed to by usart, which is the usart STATUS register.
The USART_STATUS_RXDATAV is a bitmask, this is used to extract only the value of RXDATAV which stands for RX DATa AVailable. As such the operation usart->STATUS & USART_STATUS_RXDATAV will return a true value only if the bit at position USART_STATUS_RXDATAV is 1. Otherwise this is false. (True and false are 1 and 0 in C generally.)
(uint8_t)usart->RXDATA
Is reading the data in the RXDATA register of that uart. As such it reads the currently received value.
To insure that RXDATA contains usefull new data,
while (!(usart->STATUS & USART_STATUS_RXDATAV))
;
is executed. This waits for the UART to have received data available.
The datasheet is often quite useful but if more detailed information is needed then the reference manual is the next step. Page 447 is the one about the USART. Page 474 shows the memory layout of the USART peripheral.
I'm trying to interface these SPH0645 Mics (I2S) to a STM32f767ZI board.
I have wired it correctly, now just trying to test the mic by passing the data through UART to my pc. Hoping someone can point me in the correct direction.
I have tried passing straight to the UART transmit. However I think I may need some datahandling - I am receiving from the UART, but sometimes just 0 or other times just gibberish which is not from the Mic as it still transmits even when i disconnect the mic.
I2S mic is receiving data in 24 bits in 32 bit frame, the last 8 bits are junk. The protocol is Big Endian, I am thinking that the HAL library handles this, however I am not completely sure.
uint16_t data;
while (1)
{
/* USER CODE END WHILE */
HAL_StatusTypeDef result= HAL_I2S_Receive(&hi2s1,&data,2,100);
HAL_UART_Transmit(&huart3,&data,2,100);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
What did I miss?
Check the return value of HAL_I2S_Receive(&hi2s1, &data, 1, 100)
Did you verify HAL_I2S_Receive expects uint32_t* as 2nd argument? I think it shall expect uint16_t*
Using HAL_UART_Transmit you want to transmit the data over UART. Shouldn't you pass data as an argument to HAL_UART_Transmit ?
There are quite a few similar questions, but none seem to have quite the same issue. I am connecting an STML4 MCU to a 6-axis sensor (LSM6DS3). I have successfully implemented everything in I2C, but would like the extra speed of SPI (and DMA, if I can get these first steps working...). So for a first step, I am trying to read the WHO_AM_I register (0x0F) of the device, which should reply with 0x69. Here is the code:
uint8_t who = 0;
// Sanity check/debugging aid should get 0x5D
who = 0x43 + 0x1A;
// Set SS low
GPIO_WritePin (GPIOB, LL_GPIO_PIN_7, GPIO_PIN_RESET);
// while tx buffer is in use, wait
while (!LL_SPI_IsActiveFlag_TXE(SPI1));
// Send READ command to the WHO_AM_I register
(SPI1->DR) = 0x8F;
// while rx buffer is in use, wait
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
// Get data off the register
who = (SPI1->DR);
// Wait for everything to wrap up before setting SS high again
while (LL_SPI_IsActiveFlag_BSY(SPI1));
// OK, now we can set SS high
GPIO_WritePin (GPIOB, LL_GPIO_PIN_7, GPIO_PIN_SET);
On the scope/analyzer, I see everything run as expected, including the sensor sending back 0x69. However, when I set a break on the other side of this code block, I see that who goes from 0 to 0x5D to 0xFF. It never reads the 0x69. I looked through other code examples and some people have a second transmit with the data set to some dummy value (usually 0xFF or 0x0), so I also tried that, but the sensor seems to get confused during the second attempt and who ends up being 0x48. I have tried every permutation of waiting for the RXNE/TXE/BSY flags that I could have, as well as many many other things to get the variable to correctly read the SPI1 data register, including reading other registers off the sensor, but all to no avail.
So then the question is, how does one correctly read values off this register?
I should also mention that I can successfully write to the device's registers. I can send the command I want, then read it back and see that it worked on the scope, even though I can never get the values assigned to a variable in code. I always get 0xFF.
I include a screen of my analyzer showing the sensor sending back 0x69 from a single read request, as well as the garbled mess it sends if I try the "dummy transmit" method.
SPI always (if the receiver is enabled) receives the data when you transfer.
This is the problem with the libraries that you do not know what is there. SPI is a lot easier to program using registers.
I assume that your data is 8bits.
You need to set the 1/4 (one byte) FIFO threshold during the SPI initialization by:
SPI1 -> CR2 |= SPI_CR2_FRXTH;
next you need to read the data from the FIFO after every write (you need also to force the compiler to use the correct size (in this case 8bits) load and store instructions):
*(volatile uint8_t *)&SPI1->DR = 0x8F; // write exactly 8 bits to the FIFO
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
dummy = *(volatile uint8_t *)&SPI-> DR;
*(volatile uint8_t *)&SPI1->DR = 0; // dummy write
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
who = *(volatile uint8_t *)&(SPI1->DR);
I do not know what is the point of using the LL libraries.
instead of
while (!LL_SPI_IsActiveFlag_RXNE(SPI1));
use registers
while (!(SPI1 -> SR & SPI_SR_RNE));
You can also wrap it into the function:
uint8_t SPI_ReadWrite8(SPI_TypeDef *spi, uint8_t data)
{
while(!(spi -> SR & SPI_SR_TXE));
*(volatile uint8_t *)&spi->DR = data;
while (!(spi -> SR & SPI_SR_RNE));
return *(volatile uint8_t *)&spi-> DR;
}
And
SPI_ReadWrite8(SPI1, 0x8f);
who = SPI_ReadWrite8(SPI1, 0);