Setting HX8353-E to sleep mode

Discussions related to the firmware code development
EA5JAQ
Posts: 86
Joined: Thu Jul 16, 2020 6:08 am

Setting HX8353-E to sleep mode

Post by EA5JAQ » Sun Jun 18, 2023 9:23 pm

Hello all,

I modified the OpenMDUV380 source code so whenever backlight is turned off, the screen enters the deep stand-by mode (as described in page 100 of the HX8353-E datasheet), and I'm posting it here in case anyone finds it interesting.

I haven't measured (yet) how much current is saved by turning on this deep stand-by mode. Also, minimum brightness should be set to 0%, because if backlight is still on while the screen is in sleep mode, it just appears grey. I haven't prepared the code for this case because in my version of the firmware I removed the minimum brightness option altogether, but it's just a matter of adding an "if" clause so if the user selected a minimum brightness, the display is not put into deep stand-by mode.

A future improvement could be setting a timer to sleep the screen after backlight has been turned off for several seconds, or when the Eco mode kicks in (that would prevent the screen from being turned on and off repeatedly, unless it hasn't been touched for a while, but I'm not sure if this is really necessary as the screen only takes about 30 ms to wake with this modification).

The implementation is as follows:

First I changed the displayEnableBacklight function in display.c

Code: Select all

void displayEnableBacklight(bool enable, int displayBacklightPercentageOff)
{
	if (enable)
	{
		if ((!displayIsBacklightLit()) && dispInitialised)
		{
			GPIO_InitTypeDef GPIO_InitStruct = {0};
			// Display shares its pins with the keypad, so the pind need to be put into alternate mode to work with the FSMC
			GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
			GPIO_InitStruct.Pull = GPIO_NOPULL;
			GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
			GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;

			GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
			HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

			GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
			HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

			HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);

	       		{
	        	uint8_t opts[] = { 0x01 };
	        	displayWriteCmds(HX8583_CMD_SETPWCTR, 1, opts);
	        	}
			vTaskDelay((25 / portTICK_PERIOD_MS));
	        	{
	        	uint8_t opts[] = { 0x00 };
	        	displayWriteCmds(HX8583_CMD_SETPWCTR, 1, opts);
	        	}
			displayWriteCmd(HX8583_CMD_SLPOUT);
		   	displayWriteCmd(HX8583_CMD_DISPON);

			HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);

			displayRenderRows(0, DISPLAY_NUMBER_OF_ROWS, true);

			vTaskDelay((10 / portTICK_PERIOD_MS));
		}
		displaySetBacklightIntensityPercentage(nonVolatileSettings.displayBacklightPercentage);
	}
	else
	{
		displaySetBacklightIntensityPercentage(displayBacklightPercentageOff);

		GPIO_InitTypeDef GPIO_InitStruct = {0};
		// Display shares its pins with the keypad, so the pind need to be put into alternate mode to work with the FSMC
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;

		GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
		HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

		HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);

	    	displayWriteCmd(HX8583_CMD_DISPOFF);
	    	displayWriteCmd(HX8583_CMD_SLPIN);
        	{
        	uint8_t opts[] = { 0xff, 0x83, 0x53 };
        	displayWriteCmds(HX8583_CMD_SETEXTC, 3, opts);
       	 	}
        	{
        	uint8_t opts[] = { 0x01 };
        	displayWriteCmds(HX8583_CMD_SETPWCTR, 1, opts);
        	}
        	{
        	uint8_t opts[] = { 0x03 };
        	displayWriteCmds(HX8583_CMD_SETPWCTR, 1, opts);
        	}

		HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
	}
}
NOTE: dispInitialised is a bool variable in display.c that is set to true at the end of displayInit. This prevents the display from doing weird things when the radio is turned on (the sleep wake commands were sent before the display's init function was executed). Also, the display is only woken if backlight is off (in case this function is called while the screen is on).
NOTE 2: the 25 ms delay is indicated in the datasheet (it says >20ms delay, but only 20 ms didn't work properly to me). The other 10 ms delay is to give a bit of time to the screen to properly turn on, as if that delay is removed, a white screen can be seen for a short time (and when using dark themes is quite noticeable). Maybe this second delay could go before rendering the display, but it's working fine as it is.

I also had to modify the displayRenderRows in HX8353E.c so the the display is not rendered while it's off. The menus, UI's, etc. will still update the screen buffer, but the buffer will only be sent to the display when it has been turned on from sleep but before turning on backlight. So, when backlight is off, displayRenderRows does not send any data to the display. I added another input to the function to bypass this restriction (because when the screen is turned on from sleep mode, it has to be rendered before backlight is turned on, so the user doesn't see weird things on screen).

Code: Select all

void displayRenderRows(int16_t startRow, int16_t endRow, bool forceRender)
{
	if (displayIsBacklightLit() || forceRender)
	{
		GPIO_InitTypeDef GPIO_InitStruct = {0};

		// Display controller has 8 lines per row.
		startRow *= 8;
		endRow *= 8;

		// Display shares its pins with the keypad, so the pind need to be put into alternate mode to work with the FSMC
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;

		GPIO_InitStruct.Pin = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
		HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

		HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);

	    	// Set start and end rows of the transfer
	    	{
	    	uint8_t opts[] = { 0x00, startRow, 0x00, endRow };
	    	displayWriteCmds(HX8583_CMD_RASET, sizeof(opts), opts);
	    	}

	    	displayWriteCmd(HX8583_CMD_RAMWR);

		uint8_t *framePtr = (uint8_t *)screenBuf + (DISPLAY_SIZE_X * startRow * sizeof(uint16_t));

		//if (HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0, HAL_DMA_XFER_CPLT_CB_ID, dmaCompleteCallback)== HAL_OK)
		{
			HAL_StatusTypeDef status = HAL_DMA_Start(&hdma_memtomem_dma2_stream0, (uint32_t)framePtr, LCD_FSMC_ADDR_DATA, (endRow - startRow) * DISPLAY_SIZE_X * sizeof(uint16_t));
			if (status == HAL_OK)
			{
				HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_stream0, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
				// need to wait for completion otherwise we CS gets disabled immediately.
				// This could be done using a transfer complete callback
			}
		}
		/*
		// fallback
		for(int y = 0; y < (endRow - startRow) * DISPLAY_SIZE_X * sizeof(uint16_t); y++)
		{
			*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = *framePtr++;
		}*/

		HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);

	   	*((volatile uint8_t*) LCD_FSMC_ADDR_DATA) = 0;// write 0 to the display pins , to pull them all low, so keyboard reads don't need to
	}
}
Obviously, the .h declaration and every other place where displayRenderRows is used will have to be modified (there are only 15 or so cases) to add this third input (always false except in the displayEnableBacklight modified function, where it's true to write the screen buffer data to the HX8353E when backlight is off but the screen is not in sleep mode anymore).

I implemented it in the only MD-UV3x0 release available to date, if this functions have been changed for the last betas, I don't know if it'll work.

I'll do some testing to check how much power this saves (I read somewhere in the forum that the new platforms use more current than the GD77-like platforms, so maybe this helps a bit), and also to see if any issues have been creating when RXing a signal with the screen off (but I don't think there will be any issues, as the function gives 10 ms for the screen to properly turn on before displaying anything).

Anyway, hope this helps.

73

VK3KYY
Posts: 7590
Joined: Sat Nov 16, 2019 3:25 am
Location: Melbourne, Australia

Re: Setting HX8353-E to sleep mode

Post by VK3KYY » Mon Jun 19, 2023 8:45 pm

You definitely need to measure the current by directly supplying the radio from a PSU, without the battery being connected, otherwise you can't be sure that the current consumption is less.

Make sure you do your tests with Eco mode set to 0, so the reduction in current is only being caused by the display controller

I did some tests with the gd77 and using deep sleep with that display controller did not reduce the current

I also tried using the deep sleep mode of the AT1846S but it also didn't seem to make much difference to the current consumption

EA5JAQ
Posts: 86
Joined: Thu Jul 16, 2020 6:08 am

Re: Setting HX8353-E to sleep mode

Post by EA5JAQ » Fri Jun 23, 2023 11:02 am

VK3KYY wrote:
Mon Jun 19, 2023 8:45 pm
You definitely need to measure the current by directly supplying the radio from a PSU, without the battery being connected, otherwise you can't be sure that the current consumption is less.

Make sure you do your tests with Eco mode set to 0, so the reduction in current is only being caused by the display controller
Okay, thank you. As soon as I can get my hands on a bench power supply I'll do a bit of testing.

By the way, I'm also experimenting sending data between my two MD-UV390s (at the moment only by embedding it in DMR voice frames, not following the DMR standard docs, but later I'll make it fully compliant with the standard). I found out that in every 27-byte DMR frame, 2 to 7 bytes arrive incorrectly on receive. Is this normal? Seems a bit high, as I thought these radios had forward error correction, etc. and I'm surprised the codec can even decode the voice when sometimes 25% of received DMR buffer bytes are a bit different (maybe only one or two bits in the byte are changed, but it's still different).

User avatar
F1RMB
Posts: 2623
Joined: Sat Nov 16, 2019 5:42 am
Location: Grenoble, France

Re: Setting HX8353-E to sleep mode

Post by F1RMB » Fri Jun 23, 2023 12:01 pm

Not all frames are Voice.

Cheers.
---
Daniel

G4EML
Posts: 930
Joined: Sat Nov 16, 2019 10:01 am

Re: Setting HX8353-E to sleep mode

Post by G4EML » Fri Jun 23, 2023 12:45 pm

The error correction is part of the AMBE voice frames and is encoded and decoded by the codec. So you are seeing the raw data before it has been error corrected. As far as I know the error corrected voice data is not visible outside the codec (which we do not have the source for).

I believe the published DMR data mode adds additional error checking.

The actual burst transmission and reception is handled by the HRC6000 chip, for which there is very little information. I would expect the raw error rate to be better than you quote but I don't know what the specifications of the chip are. Interestingly none of the major radio manufacturers use this chip, it only seems to be used in the low cost Chinese radios. It may well be possible to reduce the error rate by 'fine tuning' some of the settings but it would be a trial and error exercise working blind without the documentation.

Colin G4EML

EA5JAQ
Posts: 86
Joined: Thu Jul 16, 2020 6:08 am

Re: Setting HX8353-E to sleep mode

Post by EA5JAQ » Fri Jun 23, 2023 2:44 pm

Thank you both. I didn't know that error correction was applied in the codec, I thought that the received buffer was already corrected.
G4EML wrote:
Fri Jun 23, 2023 12:45 pm
I believe the published DMR data mode adds additional error checking
Yes, I saw in the DMR standard docs that data frames are 198 bits long, while voice frames are 216 (27B), and the standard has some bits allocated for FEC in the data frames. Right now I was just trying to send a 27B char string instead of the AMBE buffer and I was surprised when I saw the amount of errors on receive (maybe I exaggerated a bit how bad it was :lol:), but I didn't know that error correction was applied in the codec itself, so mystery solved.

I still have to check the (incomplete) C6000 datasheet to see if I can find out if there's any info on how to send data. I suppose the C6000 must have some kind of command to do this by itself (but good luck to me finding it).

A while ago I read somewhere in the forum that someone managed to send and receive location messages, and that's kind of what I wanted to do, to have my radios send each other automated location and status messages, kind of like APRS but using DMR data frames, not the embedded location in the LC (which requires audio to be sent). I could do this by "hijacking" the voice frames to add my data with some kind of custom error correction algorithm, but that's not an option as I want to follow the standard (and not pollute the airwaves). But right now I was just running some quick tests to learn how the code handles DMR communication.

User avatar
F1RMB
Posts: 2623
Joined: Sat Nov 16, 2019 5:42 am
Location: Grenoble, France

Re: Setting HX8353-E to sleep mode

Post by F1RMB » Fri Jun 23, 2023 4:15 pm

You could take a look at the hotspot code too.

Cheers.
---
Daniel

DL4LEX
Posts: 62
Joined: Sat Nov 16, 2019 3:09 pm

Re: Setting HX8353-E to sleep mode

Post by DL4LEX » Fri Jun 23, 2023 6:13 pm

EA5JAQ wrote:
Fri Jun 23, 2023 2:44 pm
Thank you both. I didn't know that error correction was applied in the codec, I thought that the received buffer was already corrected.
G4EML wrote:
Fri Jun 23, 2023 12:45 pm
I believe the published DMR data mode adds additional error checking
Yes, I saw in the DMR standard docs that data frames are 198 bits long, while voice frames are 216 (27B), and the standard has some bits allocated for FEC in the data frames. Right now I was just trying to send a 27B char string instead of the AMBE buffer and I was surprised when I saw the amount of errors on receive (maybe I exaggerated a bit how bad it was :lol:), but I didn't know that error correction was applied in the codec itself, so mystery solved.

I still have to check the (incomplete) C6000 datasheet to see if I can find out if there's any info on how to send data. I suppose the C6000 must have some kind of command to do this by itself (but good luck to me finding it).

A while ago I read somewhere in the forum that someone managed to send and receive location messages, and that's kind of what I wanted to do, to have my radios send each other automated location and status messages, kind of like APRS but using DMR data frames, not the embedded location in the LC (which requires audio to be sent). I could do this by "hijacking" the voice frames to add my data with some kind of custom error correction algorithm, but that's not an option as I want to follow the standard (and not pollute the airwaves). But right now I was just running some quick tests to learn how the code handles DMR communication.
I have some modified code based on an older OpenGD77 source where I am able to receive messages from other radios. It does not send any data. The received data is decoded and send to USB.
Sending data is possible but would require some more changes to the DMR code, as most as it is build around voice transmissions. I had some success but not enough time to complete this.

DMR has 3 different encodings for data frames: Data 1/1, Data 1/2 and Data 3/4.
- Data 1/1 doesn't use any encoding, so a data frame has 24 octets aka 192 data frame bits.
- Data 1/2 is using BPTC(196,96) encoding. This results in 18 octets per data frame. This encoding is also used by most other data frame types like Voice LC Header, Terminator with LC.
- Data 3/4 is using a Trellis encoding. This results in 18 octets per frame.

This is valid for unconfirmed data transmission. With confirmed data every frame has to contain a 8-bit CRC resulting in fewer octets per data frame: 22, 16 and 10.

A complete data transfer requires a data header and one or more data frames. The data header contains information about the following data frames: data type, confirmed/unconfirmed, source dmr id, receiver dmr id, data size, ...
The last data frame contains a additional 32-bit CRC covering all data frames.

The receiving radio should reply with a Response Header at least for confirmed data. The Response Header may acknowledge the transmission or contain information about wrong data frames. But this varies depending on the send Data Header.

The C6000 is handling the encoding and decoding of data frames according to data rate, 1/1 1/2 3/4. So You don't have to care about this. You always get the decoded bits on receiving and you do not have to do the encoding for transmitting.

For complete data services there are more things to consider like CSBK frames, sequence numbers in data frames, ... ETSI TS 102 361-1 has a lot of information.

If someone is willing to implement the Trellis encoder/decoder for Data 3/4 encoding, it shouldn't be too hard to implement data in hotspot mode. MMDVMHost is just expecting and sending encoding frames. It doesn't really care about voice or data. The BPTC(196, 92)-encoder is included in the source as it is used for Voice Headers and Terminator frames. So every ota received data frame has to be encoded regarding the data type and send to MMDVMHost and vice versa data frames from MMDVMHost have to be decoded so the C6000 can encode it and transmit the data frame.

Alex

EA5JAQ
Posts: 86
Joined: Thu Jul 16, 2020 6:08 am

Re: Setting HX8353-E to sleep mode

Post by EA5JAQ » Sat Jun 24, 2023 12:01 pm

DL4LEX wrote:
Fri Jun 23, 2023 6:13 pm

I have some modified code based on an older OpenGD77 source where I am able to receive messages from other radios. It does not send any data. The received data is decoded and send to USB.
Sending data is possible but would require some more changes to the DMR code, as most as it is build around voice transmissions. I had some success but not enough time to complete this.

DMR has 3 different encodings for data frames: Data 1/1, Data 1/2 and Data 3/4.
- Data 1/1 doesn't use any encoding, so a data frame has 24 octets aka 192 data frame bits.
- Data 1/2 is using BPTC(196,96) encoding. This results in 18 octets per data frame. This encoding is also used by most other data frame types like Voice LC Header, Terminator with LC.
- Data 3/4 is using a Trellis encoding. This results in 18 octets per frame.

This is valid for unconfirmed data transmission. With confirmed data every frame has to contain a 8-bit CRC resulting in fewer octets per data frame: 22, 16 and 10.

A complete data transfer requires a data header and one or more data frames. The data header contains information about the following data frames: data type, confirmed/unconfirmed, source dmr id, receiver dmr id, data size, ...
The last data frame contains a additional 32-bit CRC covering all data frames.

The receiving radio should reply with a Response Header at least for confirmed data. The Response Header may acknowledge the transmission or contain information about wrong data frames. But this varies depending on the send Data Header.

The C6000 is handling the encoding and decoding of data frames according to data rate, 1/1 1/2 3/4. So You don't have to care about this. You always get the decoded bits on receiving and you do not have to do the encoding for transmitting.

For complete data services there are more things to consider like CSBK frames, sequence numbers in data frames, ... ETSI TS 102 361-1 has a lot of information.

If someone is willing to implement the Trellis encoder/decoder for Data 3/4 encoding, it shouldn't be too hard to implement data in hotspot mode. MMDVMHost is just expecting and sending encoding frames. It doesn't really care about voice or data. The BPTC(196, 92)-encoder is included in the source as it is used for Voice Headers and Terminator frames. So every ota received data frame has to be encoded regarding the data type and send to MMDVMHost and vice versa data frames from MMDVMHost have to be decoded so the C6000 can encode it and transmit the data frame.

Alex
That’s great to know, thank you so much. I had been reviewing the DMR specification documents but didn’t get that deep in the data protocol, still have to do some reading.

It’s also good to know that the C6000 handles all the encoding and decoding, so the implementation shouldn’t be that hard (for Tx I suppose it’s just a matter of creating cases in the TS interrupt function state machine in HRC6000.c to add data transmit capability according to the specification: sending the headers, and then the data frames themselves). Shouldn’t be too hard.

I can’t speak about the hotspot feature because I’ve never used it (I have a repeater nearby) but if most of the encoding/decoding functions are implemented I suppose I could give it a go, once I fully understand the hotspot code and do some testing.

Would it be OK for you to share the modified old sources? I’m gonna have some free time over the summer and I could try to implement it in the new sources and also add the transmit capability. And obviously, I’ll post it back here. Maybe implementing SMSs is not that useful (it is my understanding that different networks use different systems and it’s a bit of a mess), but location messages to have some kind of digital automated APRS could be a useful feature to add to the newly supported radios with GPS. I have no problem trying to program this now that i’m gonna have a bit of free time.

Thanks, 73

DL4LEX
Posts: 62
Joined: Sat Nov 16, 2019 3:09 pm

Re: Setting HX8353-E to sleep mode

Post by DL4LEX » Tue Jun 27, 2023 11:24 pm

As you wrote you need some more states inside the TS interrupt function.
If you receive data from an other radio, you have to transmit the response data header. Otherwise the transmitting radio will send the data again.
And when you transmit your data, you will have to handle the received response header and maybe resend the data frames or at least part of it.

The ETSI defines a SMS data format, but Motorola and Hytera are using different types. The ETSI also defines a data format for location messages. That is different to the already implemented location data within voice streams. But again Motorola and Hytera use something different.
Sending SMS from my Hytera radio using the local Motorola repeater is nearly almost failing. With my hotspot it is working a lot better.

Sending the raw data frames is not too complicated. But handling the complete protocol requires more.

I will try to find my old source code, where I already did this. But it is more a proof of concept now as the opengd77 source changed a lot since then.

If you have any further questions maybe I can help.

Post Reply