Microcontroller 89c52 - 7 segment multiplexing display - c

I have an example program for uController written in C handling 7 segment multiplexing display.
I can access left or right part of display as following:
//declarations
unsigned char xdata left _at_ 0xFE00;
unsigned char xdata right _at_ 0xFD00;
//use
left = 0x06;
right = 0x5B;
And it will print "1" to left screen, "2" to right.
Now I'm wondering, how does that work?
Where did 0xFE00 adress came from and how it happens to display it to left screen (I know it's becouse there is '0' on P2_0 but I don't know how it is connnected to 'left' variable).

I have never programmed this beast but a look at your code, circuit diagram and the data sheet suggests that the LED pair is addressed as external memory: 8 data bits (otherwise port 0) are wired to the segment data pins and 2 address lines (otherwise port 2) are wired to the digit select pins.
The value FE00 has bit 8 low and bit 9 high. So writing to address FE00 causes 0 to be output from P2.0 (aka A8) and 1 to be output from P2.1 (aka A9), selecting the left LED, with the data presented on P0.
The value FD00 has bit 8 high and bit 9 low. So writing to address FD00 causes 1 to be output from P2.0 (aka A8) and 0 to be output from P2.1 (aka A9), selecting the right LED, and again the segment data is on P0.
The Special Function Registers will set up the required port mode.

Related

f3discovery: trying to use an lcd screen 1602 with an I2C module

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

UART communication on Atmega32A with PC

I'm a begginer in programming AVR microcontroler and I get a lot of headacke sometimes from reading the datasheets.
I'm trying to make a communication between my AVR and PC just to send some caracters and receive it on my computer.
There are two lines I don't understand from the whole program and that is:
void USART_init(void)
{
UBRRH = (uint8_t)(BAUD_PRESCALLER>>8); <---- this one!
UBRRL = (uint8_t)(BAUD_PRESCALLER); <--- and this one
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<UCSZ0)|(1<<UCSZ1)|(1<<URSEL);
}
Datasheet
Why do I have to shift BAUD_PRESCALLER with 8? If BAUD_PRESCALLER is a number and shifting that number with 8 doesn't mean the result will be zero?(Because we are shifting it too many times)
From the datasheet I understand that UBRRH contains the four most significant bits and the UBRRL contains the eight least signicant bits of the USART baut rate.(Note:UBBR is a 12-bit register)
So how actually we put all the required numbers in the UBBR register?
You have to shift it right 8 bits because the result of BAUD_PRESCALLER is larger than 8 bits. Shifting it right 8 bits gives you the most significant byte of a 16-bit value.
For example, if the value of BAUD_PRESCALAR is 0x123 - then 0x1 would be assigned to UBRRH and 0x23 would be assigned to UBRRL.
If the library was smart it could also perform sanity checking on the BAUD_PRESCALAR to make sure it fits in 16bits. If it can't, that means you cannot achieve the baud rate you want given the clock you are using. If you're UBRRx is truly 12bits, the sanity check would look something like this:
#if BAUD_PRESCALAR > 0xFFF
#error Invalid prescalar
#endif

How to read a portion of a GPIO input or assign the GPIO input to a bit vector?

I'm trying to read the inputs from a GPIO through a microblaze implemented in a Basys3 FPGA board. My GPIO inputs are coming from the 16 dip switches in the Basys3 board. Currently I'm using the following line of code to read the GPIO input to a variable.
DataRead = XGpio_DiscreteRead(&GpioDevice, 2);
where "GpioDevice" is the instance name and '2' is the channel number. This line reads the 16 switches and assigns the Hex value of the switches to the u32 variable 'DataRead'.
Is there a way to read only a portion of the GPIO input (ex: if my switches are set to 1111 0000 1010 1100, is there a way to read only 4 bits from that)
OR
is there a way to read all 16bits and assign the value to a bit vector rather than to a u32.
My goal is to use individual bits of the GPIO input to do some sort of a task.
if bit 1 = 1 then do something
if bit 2 = 0 then do something etc...

Bit banging for SPI in ARM

I am trying to read the data from FXLS8471Q 3-Axis, Linear Accelerometer using SPI. I am using bit banging method to read the data from Accelerometer. I am using LPC 2184 ARM processor. I used the following code.
unsigned char spiReadReg (const unsigned char regAddr)
{
unsigned char SPICount;
unsigned char SPIData;
SPI_CS = 1;
SPI_CK = 0;
SPIData = regAddr;
SPI_CS = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
}
SPI_MOSI = 0;
SPIData = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
SPIData <<=1;
SPI_CK = 1;
SPIData += SPI_MISO;
SPI_CK = 0;
SPIData &=(0xFE);
}
SPI_CS = 1;
return ((unsigned char)SPIData);
}
But instead of getting valid value 0x6A , I am getting garbage value.
Please help me out to solve this problem;
SPIData &=(0xFE); as pointed out in another answer, is definitely wrong as it erases the bit you just received.
However, there are other major issues with your code.
An SPI slave device sends you data by setting the value of MISO on a rising or falling clock, depending on the type of device. However, you didn't wait in your code for the value to appear on MISO.
You control the communication by setting the clock to 1 and 0. The datasheet of the accelerometer says on page 19, that
Data is sampled during the rising edge of SCLK and set up during the falling edge of SCLK.
This means that in order to read from it, your processor needs to change the clock from one to zero, thereby signaling the accelerometer to send the next bit to the MISO. This means you did the reverse, you in your code read on a rising edge while you should be reading on the falling edge. After setting the clock to zero, you have to wait a little while until the value appears on MISO, and only then should you read it and add it to your SPIData variable. Table 9, SPI timing indicates how much you have to wait: at least 500 nanoseconds. That's not much, but if your CPU runs faster than 2 MHz then if you don't use a delay, you will try to read the MISO before the accelerometer had time to properly set it.
You also forgot the slave Select, which is actually required to indicate the beginning and the end of a datagram.
Check the Figure 7. SPI Timing Diagram in the datasheet, it indicates what you are required to do and in what order, to communicate with the device using SPI.
I also suggest reading about how the rotating registers of the SPI work, because it seems from its datasheet, that the accelerometer needs to receive a well specified number of bits before useful data appears on its output. Don't forget, as you send a single bit to the device, it also has to send a bit back to you, so if it didn't decode a command yet, it can only send gibberish. Your code, as the master, can only "push" bits in, and collect the bits which "pop out" on the other side. This means you have to send a command, and then send further bits until all the answer is pushed out to you bit by bit.
If you get stuck, I think you will have much more luck getting help on https://electronics.stackexchange.com/, but instead of just putting this same code there (which seems to be blindly copied from www.maximintegrated.com and has absolutely nothing to do with the problem you try to solve), I strongly recommend trying to understand the "Figure 7. SPI Timing Diagram" I was suggesting before, and alter your code accordingly.
Without understanding how the device you try to communicate with works, you will never succeed if you just blindly copy the code from a completely different project just because it says "spi" in the title.
SPIData &=(0xFE);
The above line is causing the problem. Here the LSB is reset to 0 (which contained the data bit just taken from MISO) -- basically you are destroying the bit you just read. Omitting the line should correct the problem.
be sure to compile your function with NO optimization
as that will corrupt the bitbang operation.
this is the code from the maxum site for the spiReadReg function.
(which looks like were you got your code.
However, this is just a guide for the 'general' sequence of operations for communicating with the maxim 1481 part.
the accel. part needs several setup commands and reads completely differently
Suggest reading the app notes and white papers available at freescale.com for the specific part number.
Those app notes/white papers will indicate the sequence of commands needed for setting up a specific mode of operation and how to request/interpret the resulting data.
There are a number of device specifics that your code has not taken into account.
Per the spec sheet the first bit transmitted is a read/write indicator, followed by 8 bits of register address, followed by 7 trash bits (suggest sending all 0's for the trash bits.) followed by the data bits.
Depending on the setup commands, those data bits could be 8 bits or 14 bits or multiple registers of 8 or 14 bits per register.

Explain the C code instruction

The code below is used for programming microcontrollers. I want to know what the code below is doing. I know that '|' is OR and '&' AND but what is the whole line doing?
lcd_port = (((dat >> 4) & 0x0F)|LCD_EN|LCD_RS);
It's hard to put into context since we don't know what dat contains, but we can see that:
The data is right-shifted by 4 bits, so 11111111 becomes 00001111, for instance.
That value is AND'ed with 0x0F. This is a common trick to remove unwanted bits, since b & 1 = 1 and b & 0 = 0. Think of your number as a sequence of bits, here's a 2-byte example :
0011010100111010
&
0000000000001111
0000000000001010
Now the LCD_EN and LCD_RS flags are OR'ed. Again, this is a common binary trick, since b | 1 = 1 and b | 0 = b, so you can add flag but not remove them. So, if say LCD_EN = 0x01 and LCD_RS = 0x02,
0000000000001010
|
0000000000000011
0000000000001011
Hope that's clearer for you.
Some guesses, as you'll probably need to find chip datasheets to confirm this:-
lcd_port is probably a variable that directly maps to a piece of memory-mapped hardware - likely an alphanumeric LCD display.
The display probably takes data as four-bit 'nibbles' (hence the shift/and operations) and the higher four bits of the port are control signals.
LCD_EN is probably an abbreviation for LCD ENABLE - a control line used on the port.
LCD_RS is probably an abbreviation for LCD READ STROBE (or LCD REGISTER SELECT) - another control line used on the port. Setting these bits while writing to the port probably tells the port the kind of operation to perform.
I wouldn't be at all surprised if the hardware in use was a Hitachi HD44780 or some derivative.
It appears to be setting some data and flags on the lcd_port. The first part applies the mask 0x0F to (dat >> 4) (shift dat right 4) which is followed by applying the LCD_EN flag and then LCD_RS flag.
It is shifting the variable data four bits to the right, then masking the value with the value 15. This results in a value ranging from 0-15 (four left-most bits). This result is binary ORd with the LCD_EN and LCD_RS flags.
This code is shifting the bits of dat 4 bits to the right and then using & 0x0F to ensure it gets only those 4 least significant bits. It's then using OR to find which bits exist in that value OR LCD_EN OR LCD_RS and assigning that value to lcd_port.

Resources