Hello guys
I have a working N5110 library on my Stm32F401 which using plain C, but recently, I came across this Arduino library:
https://github.com/cbm80amiga/N5110_SPIWhat I like about this library is the extensive fonts that it has. So, I wanted to just port 2 function from this library which are (N5110_SPI::setFont) and (N5110_SPI::printChar) , and use my library to initialize the display and send data using SPI.
Bellow, is the C++ code:
//------------------------------------------------------------------------
void N5110_SPI::setFont(const uint8_t* f)
{
font = f;
xSize =-pgm_read_byte(font+0);
ySize = pgm_read_byte(font+1);
firstCh = pgm_read_byte(font+2);
lastCh = pgm_read_byte(font+3);
ySize8 = (ySize+7)/8;
minCharWd = 0;
minDigitWd = 0;
cr = 0;
invertCh = 0;
invertMask = 0xff;
}
//--------------------------------------------------------------------------
int N5110_SPI::printChar(int x, uint8_t row, uint8_t _ch)
{
int ch = _ch;
if(!font || ch<firstCh || ch>lastCh || x>=SCR_WD || row>=SCR_HT/8) return 0;
int j,i, idx = 4 + (ch - firstCh)*(xSize*ySize8+1);
int wd = pgm_read_byte(font + idx++);
int wdL = 0, wdR = 1; // default spacing before and behind char
if((*isNumberFun)(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}
if(x+wd+wdL+wdR>SCR_WD) wdR = max(SCR_WD-x-wdL-wd, 0);
if(x+wd+wdL+wdR>SCR_WD) wd = max(SCR_WD-x-wdL, 0);
if(x+wd+wdL+wdR>SCR_WD) wdL = max(SCR_WD-x, 0);
for(j=0; j<ySize8; j++) {
gotoXY(x, row+j);
setDat();
startCS();
if(!invertCh) {
for(i=0; i<wdL; i++) sendData(0);
for(i=0; i<wd; i++) sendData(pgm_read_byte(font+idx+i*ySize8+j));
for(i=0; i<wdR; i++) sendData(0);
} else {
for(i=0; i<wdL; i++) sendData(invertMask);
for(i=0; i<wd; i++) sendData(pgm_read_byte(font+idx+i*ySize8+j)^invertMask);
for(i=0; i<wdR; i++) sendData(invertMask);
}
stopCS();
}
return wd+wdL+wdR;
}
My code is below, that compiles, but doesn't display anything on the display:
//------------------------------------------------------------------
void N5110_setFont(const uint8_t* f)
{
font = f;
xSize =-(font[0]+0);
ySize = (font[0]+1);
firstCh = (font[0]+2);
lastCh = (font[0]+3);
ySize8 = (ySize+7)/8;
minCharWd = 0;
minDigitWd = 0;
cr = 0;
invertCh = 0;
invertMask = 0xff;
}
int N5110_printChar(int x, uint8_t row, uint8_t _ch)
{
int ch = _ch;
if(!font || ch<firstCh || ch>lastCh || x>=SCR_WD || row>=SCR_HT/8) return 0;
int j,i, idx = 4 + (ch - firstCh)*(xSize*ySize8+1);
int wd = (font[0] + idx++);
int wdL = 0, wdR = 1; // default spacing before and behind char
if((*isNumberFun)(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}
if(x+wd+wdL+wdR>SCR_WD) wdR = max(SCR_WD-x-wdL-wd, 0);
if(x+wd+wdL+wdR>SCR_WD) wd = max(SCR_WD-x-wdL, 0);
if(x+wd+wdL+wdR>SCR_WD) wdL = max(SCR_WD-x, 0);
for(j=0; j<ySize8; j++) {
gotoXY(x, row+j);
setDat();
startCS();
if(!invertCh) {
for(i=0; i<wdL; i++) glcd_data(0);
for(i=0; i<wd; i++) glcd_data((font[0]+idx+i*ySize8+j));
for(i=0; i<wdR; i++) glcd_data(0);
} else {
for(i=0; i<wdL; i++) glcd_data(invertMask);
for(i=0; i<wd; i++) glcd_data((font[0]+idx+i*ySize8+j)^invertMask);
for(i=0; i<wdR; i++) glcd_data(invertMask);
}
stopCS();
}
return wd+wdL+wdR;
}
for the "max" function, because it doesn't exist in C, I found online which is:
int max(int a, int b) {
return (a > b) ? a : b;
}
I sure something is wrong with my code, but I can't figure it out by my self. Please, guys don't be hard on me, as I'm just a hobbyist, not an expert in programming. Can someone guide me how to port these 2 functions to plain C please?
Thank you in advance
I sure something is wrong with my code
What exactly? In which way? How do you know? Are there any visible signs of it being wrong?
xSize =-pgm_read_byte(font+0);
ySize = pgm_read_byte(font+1);
firstCh = pgm_read_byte(font+2);
lastCh = pgm_read_byte(font+3);
xSize =-(font[0]+0);
ySize = (font[0]+1);
firstCh = (font[0]+2);
lastCh = (font[0]+3);
At least here is mistake. font is a pointer.
Original code reads byte from address font+x, while your code reads byte from address font and then add x to the result of read operation.
The correct replacement should be something like this:
xSize =-(uint8_t)font[0];
ySize = (uint8_t)font[1];
firstCh = (uint8_t)font[2];
lastCh = (uint8_t)font[3];
uint8_t is defined in stdint.h, this is cross-platform unsigned byte.
You can't just take these two functions and translate them to C. You have to analyze the functionality and also make sure the supporting functions are taken into account.
The comment of radiolistener points out what I mean with my remark. You overlooked what the pgm_read_byte function does, and what the type of font is. (const uint8_t* font;)
Converting it to C also means you have to declare a lot of global variables, and it would be clearer when a structure is used for this. So convert the variables in the class to a structure and make that a global variable.
Edit: For better help you should attach your full project as an archive to your original post. The code sniplets don't show what is done in for instance "glcd_data".
I sure something is wrong with my code
What exactly? In which way? How do you know? Are there any visible signs of it being wrong?
Well he did write that nothing is shown on the display, which to me seems something is indeed wrong.
My code is below, that compiles, but doesn't display anything on the display:
Hello
I attached the whole project in the first post.
I have looked at the code and have to say that it is a bit of a mess.
You should try to adapt a more structured and cleaner way of programming. This will improve readability and also maintainability.
There might be an issue with the SPI transfer not checking for the correct finishing state. Take a look at a project of mine
here. In the file "stm32f103_nrf905.c" there is the function "nrf905_write_spi" that sends data through an SPI port that works for sure. During development of that code I ran into some issues with the behavior of the SPI peripheral that was not clearly specified in the manuals and needed to make sure to wait for a proper finish after the data was send to keep it working.
The archive you attached is missing a make file and a linker script.
I have looked at the code and have to say that it is a bit of a mess.
You should try to adapt a more structured and cleaner way of programming. This will improve readability and also maintainability.
There might be an issue with the SPI transfer not checking for the correct finishing state. Take a look at a project of mine here. In the file "stm32f103_nrf905.c" there is the function "nrf905_write_spi" that sends data through an SPI port that works for sure. During development of that code I ran into some issues with the behavior of the SPI peripheral that was not clearly specified in the manuals and needed to make sure to wait for a proper finish after the data was send to keep it working.
The archive you attached is missing a make file and a linker script.
Thank you for taking a look to the code,but I can guarantee that the spi function is working fine.Try to send something with the command glcd_data(0xff) it will show a bar in the display.
Thank you for taking a look to the code,but I can guarantee that the spi function is working fine.Try to send something with the command glcd_data(0xff) it will show a bar in the display.
I don't have a setup ready to test this, but I will take your word for that part working. Still leaves you with non working code. In what you attached the error with the font reading is still faulty.
Check radiolistener his post and fix that first and see what else is not reading the font data in a correct manner.
First of all, check the compiler warnings, for sure you'll be getting plenty of them, like the mistake with the font pointer.
They're not there to just be ignored!
it could be the way your code refers to the font variable, i often found that in one library i would refer to it as font, another as font[] another as *font (pointer),
a trick i also learned is that in a function thats not working you should also send out the data to serial port to confirm your function has access to the data, i've had situations where i did not pass the pointer too the data but just a empty var and was able to confirm it by sending it to the serial port.
darkspr1te
Thank you for taking a look to the code,but I can guarantee that the spi function is working fine.Try to send something with the command glcd_data(0xff) it will show a bar in the display.
have you fix your code as advised by radiolistener? its called semantic error and semantic errors wont show up in compiler error nor warning.
Yes! I have tried his suggestion, but still no joy!
I just found out how the font arrays are formatted, so I'm going to write my own char and string functions. Thank you everybody for your help
Yes! I have tried his suggestion, but still no joy!
Did you fixed the same mistake which is present all around your code?
For example, your code has the same issue in this line:
C++
int wd = pgm_read_byte(font + idx++);
Your incorrect replacement:
int wd = (font[0] + idx++);
Should be:
int wd = font[ idx++ ];
Also exactly the same mistake in this line:
for(i=0; i<wd; i++) glcd_data((font[0]+idx+i*ySize8+j));
should be:
for(i=0; i<wd; i++) sendData( font[ idx+i*ySize8+j ]);
and exactly the same mistake in this line:
for(i=0; i<wd; i++) glcd_data((font[0]+idx+i*ySize8+j)^invertMask);
should be:
for(i=0; i<wd; i++) sendData( font[ idx+i*ySize8+j] ^ invertMask);
As you can see, there is exactly the same mistake all around your code.
In order to avoid it, I recommend you to copy original C++ code and just implement function pgm_read_byte to avoid code changes:
uint8_t pgm_read_byte(uint8_t* ptr) {
return *ptr;
}
Here is example:
uint8_t pgm_read_byte(uint8_t* ptr) {
return *ptr;
}
void N5110_setFont(const uint8_t* f)
{
font = f;
xSize =-pgm_read_byte(font+0);
ySize = pgm_read_byte(font+1);
firstCh = pgm_read_byte(font+2);
lastCh = pgm_read_byte(font+3);
ySize8 = (ySize+7)/8;
minCharWd = 0;
minDigitWd = 0;
cr = 0;
invertCh = 0;
invertMask = 0xff;
}
int N5110_printChar(int x, uint8_t row, uint8_t _ch)
{
int ch = _ch;
if(!font || ch<firstCh || ch>lastCh || x>=SCR_WD || row>=SCR_HT/8) return 0;
int j,i, idx = 4 + (ch - firstCh)*(xSize*ySize8+1);
int wd = pgm_read_byte(font + idx++);
int wdL = 0, wdR = 1; // default spacing before and behind char
if((*isNumberFun)(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}
if(x+wd+wdL+wdR>SCR_WD) wdR = max(SCR_WD-x-wdL-wd, 0);
if(x+wd+wdL+wdR>SCR_WD) wd = max(SCR_WD-x-wdL, 0);
if(x+wd+wdL+wdR>SCR_WD) wdL = max(SCR_WD-x, 0);
for(j=0; j<ySize8; j++) {
gotoXY(x, row+j);
setDat();
startCS();
if(!invertCh) {
for(i=0; i<wdL; i++) sendData(0);
for(i=0; i<wd; i++) sendData(pgm_read_byte(font+idx+i*ySize8+j));
for(i=0; i<wdR; i++) sendData(0);
} else {
for(i=0; i<wdL; i++) sendData(invertMask);
for(i=0; i<wd; i++) sendData(pgm_read_byte(font+idx+i*ySize8+j)^invertMask);
for(i=0; i<wdR; i++) sendData(invertMask);
}
stopCS();
}
return wd+wdL+wdR;
}
Also, make sure that the font content is properly linked and the pointer is correctly pointing on the memory which contains the font data.
int wd = pgm_read_byte(font + idx++);
This isn't even a C++ thing, although it IS an Arduino thing.
pgm_read_byte() is needed on an AVR to read data from the program memory, which is in a separate address space.
a definition of pgm_read_byte() (and related stuff) has been propagated to other platforms, even when they don't need it, for reasons of portability and compatibility.
In the case of STM boards, the pgm_ macros are defined somewhere like .../packages/STMicroelectronics/hardware/stm32/2.3.0/cores/arduino/avr/pgmspace.h, and look like:
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
You would have been better off copying that (entirely plain C) file into your STM C project, instead of editing the individual pgm_read_byte() calls (especially since you did it incorrectly)
(but probably all you need is the above definition of pgm_read_byte() )
int wd = pgm_read_byte(font + idx++);
This isn't even a C++ thing, although it IS an Arduino thing.
pgm_read_byte() is needed on an AVR to read data from the program memory, which is in a separate address space.
a definition of pgm_read_byte() (and related stuff) has been propagated to other platforms, even when they don't need it, for reasons of portability and compatibility.
In the case of STM boards, the pgm_ macros are defined somewhere like .../packages/STMicroelectronics/hardware/stm32/2.3.0/cores/arduino/avr/pgmspace.h, and look like:
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
You would have been better off copying that (entirely plain C) file into your STM C project, instead of editing the individual pgm_read_byte() calls (especially since you did it incorrectly)
(but probably all you need is the above definition of pgm_read_byte() )
Thank you very much, westfw. So, by defining the pgm_read_byte you suggested, I made a lot of progress, but I had to comment the (*isNumberFun) section out to make the code work:
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
const uint8_t* font;
uint8_t xSize;
uint8_t ySize;
uint8_t ySize8;
uint8_t firstCh;
uint8_t lastCh;
uint8_t minCharWd;
uint8_t minDigitWd;
uint8_t cr; // carriage return mode for printStr
uint8_t dualChar;
uint8_t invertCh,invertMask;
bool (*isNumberFun)(uint8_t ch);
//----max function-------------------------
int max(int a, int b) {
return (a > b) ? a : b;
}
//--------------------------------------
bool N5110_isNumber(uint8_t ch)
{
return isdigit(ch) || ch==' ';
}
//--------------------------------
//---------------------------------
void setDat() {gpio_write(DC, 1); }
void setCmd() { gpio_write(DC, 0); }
void startCS() { gpio_write(CS, 0); }
void stopCS() { gpio_write(CS, 1); }
//------------------------------------------------------------------
void N5110_setFont(const uint8_t* f)
{
font = f;
xSize =-pgm_read_byte(font+0);
ySize = pgm_read_byte(font+1);
firstCh = pgm_read_byte(font+2);
lastCh = pgm_read_byte(font+3);
ySize8 = (ySize+7)/8;
minCharWd = 0;
minDigitWd = 0;
cr = 0;
invertCh = 0;
invertMask = 0xff;
}
int N5110_printChar(int x, uint8_t row, uint8_t _ch)
{
int ch = _ch;
if(!font || ch<firstCh || ch>lastCh || x>=SCR_WD || row>=SCR_HT/8) return 0;
int j,i, idx = 4 + (ch - firstCh)*(xSize*ySize8+1);
int wd = pgm_read_byte(font + idx++);
int wdL = 0, wdR = 1; // default spacing before and behind char
/* if((*isNumberFun)(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}*/
if(x+wd+wdL+wdR>SCR_WD) wdR = max(SCR_WD-x-wdL-wd, 0);
if(x+wd+wdL+wdR>SCR_WD) wd = max(SCR_WD-x-wdL, 0);
if(x+wd+wdL+wdR>SCR_WD) wdL = max(SCR_WD-x, 0);
for(j=0; j<ySize8; j++) {
gotoXY(x, row+j);
setDat();
startCS();
if(!invertCh) {
for(i=0; i<wdL; i++) glcd_data(0);
for(i=0; i<wd; i++) glcd_data(pgm_read_byte(font+idx+i*ySize8+j));
for(i=0; i<wdR; i++) glcd_data(0);
} else {
for(i=0; i<wdL; i++) glcd_data(invertMask);
for(i=0; i<wd; i++) glcd_data(pgm_read_byte(font+idx+i*ySize8+j)^invertMask);
for(i=0; i<wdR; i++) glcd_data(invertMask);
}
stopCS();
}
return wd+wdL+wdR;
}
Where is isNumberFun declared?
Maybe you can use a simple ASCII digit test.
Where is isNumberFun declared?
Here's the complete .c file
#include <stm32f401xc.h>
#include <stdint.h>
#include "delay.h"
#include "nokia_glcd.h"
#include "gpio.h"
#include "ctype.h"
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define ALIGNMENT \
if(x==-1)\
x = SCR_WD-wd; \
else if(x<0) \
x = (SCR_WD-wd)/2; \
if(x<0) x=0; \
if(x>=SCR_WD || y8>=SCR_HT/8) return 0; \
if(x+wd>SCR_WD) wd = SCR_WD-x; \
if(y8+ht8>SCR_HT/8) ht8 = SCR_HT/8-y8
const uint8_t* font;
uint8_t xSize;
uint8_t ySize;
uint8_t ySize8;
uint8_t firstCh;
uint8_t lastCh;
uint8_t minCharWd;
uint8_t minDigitWd;
uint8_t cr; // carriage return mode for printStr
uint8_t dualChar;
uint8_t invertCh,invertMask;
bool (*isNumberFun)(uint8_t ch);
//----max function-------------------------
int max(int a, int b) {
return (a > b) ? a : b;
}
//--------------------------------------
bool N5110_isNumber(uint8_t ch)
{
return isdigit(ch) || ch==' ';
}
//--------------------------------
//---------------------------------
void setDat() {gpio_write(DC, 1); }
void setCmd() { gpio_write(DC, 0); }
void startCS() { gpio_write(CS, 0); }
void stopCS() { gpio_write(CS, 1); }
//uint8_t x,y;
/* The function sends a byte of data through SPI */
/* argument d: the byte to be sent */
/* return value: the received data */
uint8_t spi2_transfer(uint8_t d)
{
gpio_write(CS,0);
SPI2->DR = d; /* send the contents of d */
while((SPI2->SR&(1<<0)) == 0); /* wait until RXNE is set */
gpio_write(CS,1);
return SPI2->DR; /* return the received data */
}
void glcd_cmd(uint8_t cmd)
{
gpio_write(DC,0);
spi2_transfer(cmd);
}
void glcd_data(uint8_t data)
{
gpio_write(DC,1);
spi2_transfer(data);
}
void glcd_init(void)
{
//--------------------------------------
//isNumberFun = &isNumber;
cr = 0;
font = NULL;
dualChar = 0;
//--------------------------------------
RCC->APB1ENR |=RCC_APB1ENR_SPI2EN;
gpio_output(CS);
gpio_output(DC);
gpio_output(RESET);
gpio_init(MOSI, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,GPIO_PULL_NONE, 5);
gpio_init(SLK, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,GPIO_PULL_NONE, 5);
SPI2->CR1 = 0x35C; /* SPE = 1, BR = 3, FFD = 0, SSI and SSM = 1 */
//GPIOB->BSRR = (1<<GLCD_CS)|(1<<GLCD_RESET); /* make CS and RESET pins high */
SPI2->CR1 = 0x35C; /* SPE = 1, BR = 3, FFD = 0, SSI and SSM = 1 */
gpio_write(DC, 1);
gpio_write(RESET, 1); /* make CS and RESET pins high */
Delay_ms(10);
gpio_write(RESET, 0); /* reset the LCD (RESET = 0) */
Delay_ms(70);
gpio_write(RESET, 1); /* release the RESET pin */
glcd_cmd(0x21); /* switch to extended command mode */
glcd_cmd(0x06); /* set temp. coefficient 2 */
glcd_cmd(0x13); /* set LCD bias mode 1:48 */
glcd_cmd(0xC2); /* set VOP to 7V (VOP = 66) */
glcd_cmd(0x20); /* switch to basic mode */
glcd_cmd(0x0C); /* set lcd display to normal mode */
}
void gotoXY(uint8_t x, uint8_t y)
{
glcd_cmd(0x80 | x); /* set x */
glcd_cmd(0x40 | y); /* set y (bank) */
}
void glcd_setcontrast(uint8_t contrast)
{
/* Max Contrast */
if(contrast > 0x7F) contrast = 0x7F;
glcd_cmd(0x21);
glcd_cmd(0x80 | contrast);
glcd_cmd(0x20);
}
void glcd_clear(void)
{
uint16_t i;
gotoXY(0, 0);
for (i = 0 ; i < 504 ; i++)
glcd_data(0x00);
}
//------------------------------------------------------------------
void N5110_setFont(const uint8_t* f)
{
font = f;
xSize =-pgm_read_byte(font+0);
ySize = pgm_read_byte(font+1);
firstCh = pgm_read_byte(font+2);
lastCh = pgm_read_byte(font+3);
ySize8 = (ySize+7)/8;
minCharWd = 0;
minDigitWd = 0;
cr = 0;
invertCh = 0;
invertMask = 0xff;
}
int N5110_printChar(int x, uint8_t row, uint8_t _ch)
{
int ch = _ch;
if(!font || ch<firstCh || ch>lastCh || x>=SCR_WD || row>=SCR_HT/8) return 0;
int j,i, idx = 4 + (ch - firstCh)*(xSize*ySize8+1);
int wd = pgm_read_byte(font + idx++);
int wdL = 0, wdR = 1; // default spacing before and behind char
/* if((*isNumberFun)(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}*/
if(x+wd+wdL+wdR>SCR_WD) wdR = max(SCR_WD-x-wdL-wd, 0);
if(x+wd+wdL+wdR>SCR_WD) wd = max(SCR_WD-x-wdL, 0);
if(x+wd+wdL+wdR>SCR_WD) wdL = max(SCR_WD-x, 0);
for(j=0; j<ySize8; j++) {
gotoXY(x, row+j);
setDat();
startCS();
if(!invertCh) {
for(i=0; i<wdL; i++) glcd_data(0);
for(i=0; i<wd; i++) glcd_data(pgm_read_byte(font+idx+i*ySize8+j));
for(i=0; i<wdR; i++) glcd_data(0);
} else {
for(i=0; i<wdL; i++) glcd_data(invertMask);
for(i=0; i<wd; i++) glcd_data(pgm_read_byte(font+idx+i*ySize8+j)^invertMask);
for(i=0; i<wdR; i++) glcd_data(invertMask);
}
stopCS();
}
return wd+wdL+wdR;
}
Delete all references to isNumberFun and isNumber.
Then:
}
//--------------------------------------------------------------------------
int N5110_SPI_printChar(int x, uint8_t row, uint8_t _ch)
{
(...)
if(N5110_isNumber(ch)) {
if(minDigitWd>wd) {
wdL = (minDigitWd-wd)/2;
wdR += (minDigitWd-wd-wdL);
}
} else
if(minCharWd>wd) {
wdL = (minCharWd-wd)/2;
wdR += (minCharWd-wd-wdL);
}
Frankly, just use u8g2!