Help porting 2 functions from C++ to C - Page 1
Forum section: electronics / general

author: kgavionics
author_rank: Regular Contributor
timestamp: May 02, 2024, 01:00:41 pm
content: Hello guysI 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: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 charif((*isNumberFun)(ch)) {if(minDigitWd>wd) {wdL = (minDigitWd-wd)/2;wdR += (minDigitWd-wd-wdL);}} elseif(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:Code://------------------------------------------------------------------void N5110_setFont(const uint8_t* f){font = f;xSize =-(font+0);ySize = (font+1);firstCh = (font+2);lastCh = (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 = (font + idx++);int wdL = 0, wdR = 1; // default spacing before and behind charif((*isNumberFun)(ch)) {if(minDigitWd>wd) {wdL = (minDigitWd-wd)/2;wdR += (minDigitWd-wd-wdL);}} elseif(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+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+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:Code: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

author: eutectique
author_rank: Frequent Contributor
timestamp: May 02, 2024, 01:33:13 pm
content: Quote from: kgavionics on May 02, 2024, 01:00:41 pmWhat exactly? In which way? How do you know? Are there any visible signs of it being wrong?

author: radiolistener
author_rank: Super Contributor
timestamp: May 02, 2024, 01:40:26 pm
content: Quote from: kgavionics on May 02, 2024, 01:00:41 pmAt 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:Code:xSize =-(uint8_t)font;ySize = (uint8_t)font;firstCh = (uint8_t)font;lastCh = (uint8_t)font;uint8_t is defined in stdint.h, this is cross-platform unsigned byte.

author: pcprogrammer
author_rank: Super Contributor
timestamp: May 02, 2024, 01:51:47 pm
content: 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".

author: pcprogrammer
author_rank: Super Contributor
timestamp: May 02, 2024, 01:54:15 pm
content: Quote from: eutectique on May 02, 2024, 01:33:13 pmWell he did write that nothing is shown on the display, which to me seems something is indeed wrong.Quote

author: kgavionics
author_rank: Regular Contributor
timestamp: May 02, 2024, 03:24:39 pm
content: HelloI attached the whole project in the first post.

author: pcprogrammer
author_rank: Super Contributor
timestamp: May 02, 2024, 04:13:38 pm
content: 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 minehere. 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.

author: kgavionics
author_rank: Regular Contributor
timestamp: May 02, 2024, 05:45:14 pm
content: Quote from: pcprogrammer on May 02, 2024, 04:13:38 pmThank 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.

author: pcprogrammer
author_rank: Super Contributor
timestamp: May 02, 2024, 06:05:49 pm
content: Quote from: kgavionics on May 02, 2024, 05:45:14 pmI 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 02, 2024, 08:54:13 pm
content: 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!

author: darkspr1te
author_rank: Frequent Contributor
timestamp: May 03, 2024, 12:30:19 pm
content: 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

author: Mechatrommer
author_rank: Super Contributor
timestamp: May 03, 2024, 04:07:47 pm
content: Quote from: kgavionics on May 02, 2024, 05:45:14 pmhave you fix your code as advised by radiolistener? its called semantic error and semantic errors wont show up in compiler error nor warning.

author: kgavionics
author_rank: Regular Contributor
timestamp: May 03, 2024, 05:38:11 pm
content: Yes! I have tried his suggestion, but still no joy!

author: kgavionics
author_rank: Regular Contributor
timestamp: May 03, 2024, 11:18:46 pm
content: 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

author: radiolistener
author_rank: Super Contributor
timestamp: May 04, 2024, 12:00:28 am
content: Quote from: kgavionics on May 03, 2024, 05:38:11 pmDid you fixed the same mistake which is present all around your code?For example, your code has the same issue in this line:C++Code:int wd = pgm_read_byte(font + idx++);Your incorrect replacement:Code:int wd = (font + idx++);Should be:Code:int wd = font[ idx++ ];Also exactly the same mistake in this line:Code:for(i=0; i<wd; i++) glcd_data((font+idx+i*ySize8+j));should be:Code:for(i=0; i<wd; i++) sendData( font[ idx+i*ySize8+j ]);and exactly the same mistake in this line:Code:for(i=0; i<wd; i++) glcd_data((font+idx+i*ySize8+j)^invertMask);should be:Code: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:Code:uint8_t pgm_read_byte(uint8_t* ptr) {return *ptr;}Here is example:Code: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 charif((*isNumberFun)(ch)) {if(minDigitWd>wd) {wdL = (minDigitWd-wd)/2;wdR += (minDigitWd-wd-wdL);}} elseif(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.

author: westfw
author_rank: Super Contributor
timestamp: May 04, 2024, 12:36:06 am
content: QuoteThis 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:Code:#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() )

author: kgavionics
author_rank: Regular Contributor
timestamp: May 04, 2024, 06:43:59 pm
content: Quote from: westfw on May 04, 2024, 12:36:06 amThank 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:Code:#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 printStruint8_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);}} elseif(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;}

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 04, 2024, 06:52:44 pm
content: Where is isNumberFun declared?

author: m k
author_rank: Super Contributor
timestamp: May 04, 2024, 07:17:29 pm
content: Maybe you can use a simple ASCII digit test.

author: kgavionics
author_rank: Regular Contributor
timestamp: May 04, 2024, 08:36:05 pm
content: Quote from: DavidAlfa on May 04, 2024, 06:52:44 pmHere's the complete .c fileCode:#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-y8const 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 printStruint8_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);}} elseif(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;}

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 04, 2024, 10:03:01 pm
content: Delete all references to isNumberFun and isNumber.Then:Code:}//--------------------------------------------------------------------------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);}} elseif(minCharWd>wd) {wdL = (minCharWd-wd)/2;wdR += (minCharWd-wd-wdL);}Frankly, just use u8g2!

author: langwadt
author_rank: Super Contributor
timestamp: May 04, 2024, 10:08:40 pm
content: https://en.wikipedia.org/wiki/Cargo_cult_programming

author: kgavionics
author_rank: Regular Contributor
timestamp: May 05, 2024, 04:01:43 am
content: Quote from: langwadt on May 04, 2024, 10:08:40 pmThank you for link! I Learned something new today 😜😜😜😜