This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LM8330: PWM just doesn't want to work

Part Number: LM8330

My problem is the functioning of the PWM0 of the LM8330.
I am developing, for one of our products, a 5x10 keyboard driver, based on the lm8330 component using, to signal the events, the interrupt on the KPY11 column not used in the matrix.
In addition I need to use PWM0 on the other KPY10 column, also not used by the keyboard, for buzzer or back light regulation.
The keyboard works fine, while PWM0 just doesn't want to work.
I attach source code used to initialize lm8330 component. I want to clarify that I found any topic of my interest in the forum but it was not enough to make my problem understood.
Looking in the forum, I also found many inaccuracies on the way to write the word patterns of the pwm script. Sometimes the write order was reported as first LSB and then MSB, other times first MSB and then LSB.
I think it should be MSB followed by LSB in a continuous write. At least I hope.
In the attached source code there are 3 scripts, but none, as indicated in the comment, works as expected.
I am desperate, because I have run endless tests, in all combinations, without any positive results.
One thing only to report, on the keyboard. I had to repeat the command "CLKMODE" to set the "operating mode", otherwise the keyboard does not work, as if there was some instruction, in the keyboard programming, which puts it in sleep mode.
Thanks to those who can help me.

#define LM8330_CMD_SET_KBDSETTLE	0x01 /* Set KBDSETTLE time. Initial time for keys to settle, before the key-scan process is started*/
#define LM8330_CMD_SET_KBDBOUNCE	0x02 /* Set debouncing time. */
#define LM8330_CMD_SET_KBDSIZE		0x03 /* Set keypad size. Defines the physical keyboard matrix size*/
#define LM8330_CMD_SET_KBDDEDCFG	0x04 /* Set Dedicated Key Register. Defines if a key is used as a standard keyboard/GPIO pin or whether it is used as dedicated key input*/
#define LM8330_CMD_SET_KBDDEDCFG0	0x04 /* Set Dedicated Key Register. Defines if a key is used as a standard keyboard/GPIO pin or whether it is used as dedicated key input*/
#define LM8330_CMD_SET_KBDDEDCFG1	0x05 /* Set Dedicated Key Register. Defines if a key is used as a standard keyboard/GPIO pin or whether it is used as dedicated key input*/
#define LM8330_CMD_READ_KBDRIS		0x06 /* Get Keyboard Raw Interrupt Status register. Returns the status of stored keyboard interrupts*/
#define LM8330_CMD_READ_KINT		0x06
#define LM8330_CMD_READ_KBDMIS		0x07 /* Get Keypad Masked Interrupt Status Register. Returns the status on masked keyboard interrupts after masking with the KBDMSK register*/
#define LM8330_CMD_SET_KBDIC		0x08 /* Set Keypad Interrupt Clear Register. Setting these bits clears Keypad active Interrupts */
#define LM8330_CMD_SET_KBDMSK		0x09 /* Set Keypad Interrupt Mask Register. Configures masking of keyboard interrupts. Masked interrupts do not trigger an event on the Interrupt output */
#define LM8330_CMD_READ_KBDCODE0	0x0B /* Read the first detected key. */
#define LM8330_CMD_READ_KBDCODE1	0x0C /* Read the second detected key. */
#define LM8330_CMD_READ_KBDCODE2	0x0D /* Read the third detected key. */
#define LM8330_CMD_READ_KBDCODE3	0x0E /* Read the forth detected key. */
#define LM8330_CMD_READ_EVTCODE		0x10 /* KRead key Event from Code Register */

#define LM8330_CMD_SET_TIMCFG0		0x60 /* Set PWM Timer 0 Configuration Register. This register configures interrupt masking of the associated PWM channel*/
#define LM8330_CMD_SET_PWMCFG0		0x61 /* Set PWM Timer 0 Conf iguration Control Register. This register defines interrupt masking and the output behavior for the associated PWM channel */
#define LM8330_CMD_SET_PWMCFG1		0x69 /* Set PWM Timer 1 Conf iguration Control Register. This register defines interrupt masking and the output behavior for the associated PWM channel */
#define LM8330_CMD_SET_PWMCFG2		0x71 /* Set PWM Timer 2 Conf iguration Control Register. This register defines interrupt masking and the output behavior for the associated PWM channel */
#define LM8330_CMD_SET_TIMSWRES		0x78 /* Set PWM Timer Software Reset Registers. Reset control on all PWM timers */
#define LM8330_CMD_READ_TIMRIS		0x7A /* Read PWM Timer Interrupt Status Register. This register returns the raw interrupt status from the PWM timers 0,1 and 2.*/
#define LM8330_CMD_READ_TIMMIS		0x7B /* Read PWM Timer Masked Interrupt Status Register. This register returns the masked interrupt status from the PWM timers 0, 1 and 2. */
#define LM8330_CMD_SET_TIMIC		0x7C /* Set PWM Timer Interrupt Clear Register. This register clears timer and pattern interrupts. */
#define LM8330_CMD_SET_PWMWP		0x7D /* Set PWM Timer Pattern Pointer Register. Pointer to the pattern position inside the configuration register, which will be overwritten by the next write access to be PWMCFG register */
#define LM8330_CMD_SET_PWMCFG		0x7E /* PWM Script Register. Two-byte pattern storage register for a PWM script command indexed by PWMWP. PWMWP is automatically incremented*/
#define LM8330_CMD_SET_I2CSA		0x80 /* Set Slave Address Register. The address is internally applied after the next I2C STOP*/
#define LM8330_CMD_READ_MFGCODE		0x80 /* Read Manufacturer Code Register*/
#define LM8330_CMD_READ_SWREV		0x81 /* Read Software revision code of the LM8330 */
#define LM8330_CMD_SWRESET			0x81 /* Software Reset Register. The reset is only applied if the supplied parameter has the inverted value as SWBIT*/
#define LM8330_CMD_RSTCTRL			0x82 /* Software reset of specific parts of the LM8330 */
#define LM8330_CMD_RSTINTCLR		0x84 /* Clear NO Init/Power-On Interrupt Register . This register is used to clear the PORIRQ Interrupt. This interrupt is set every time the device returns from RESET (either POR, HW or SW Reset).*/

#define LM8330_CMD_CLKMODE			0x88 /* Clock Mode Register. This register controls the current operating mode of the LM8330 device. */
#define LM8330_CMD_CLKEN			0x8A /* Clock Enable Register. Controls the clock to different functional units. It is used to enable the functional blocks globally and independently */
#define LM8330_CMD_AUTOSLP			0x8B /* Auto-sleep Enable Register. This register controls the Auto-Sleep function of the LM8330 device */
#define LM8330_CMD_AUTOSLPTIL		0x8C /* Auto-Sleep Time Register Low byte. This register defines the activity time. If this time passes without any processing events then the device enters into sleep-mode, but only if AUTOSLP.ENABLE bit is set to 1. */
#define LM8330_CMD_AUTOSLPTIH		0x8D /* Auto-Sleep Time Register High byte*/

#define LM8330_CMD_READ_IRQST		0x91 /* Read Interrupt Global Interrupt Status Register. Returns the interrupt status from various on-chip function blocks */
#define LM8330_CMD_READ_INT			0x91

#define LM8330_CMD_SET_IOCFG		0xA7 /* Input/Output Pin Mapping Configuration Register. Configures usage of KPY[11:8] if not used for Keypad. */
#define LM8330_CMD_SET_IOPC0		0xAA /* Pull Resistor Configuration Register 0. Defines the pull resistor configuration for balls KPX[7:0]. */
#define LM8330_CMD_SET_IOPC1		0xAC /* Pull Resistor Configuration Register 1. Defines the pull resistor configuration for balls KPY[7:0].. */
#define LM8330_CMD_SET_IOPC2		0xAE /* Pull Resistor Configuration Register 2. Defines the pull resistor configuration for balls KPY[11:8].. */



/* Interrupt global status. */
#define INT_GPIOIRQ					0x01 /* GPIO interrupt */
#define INT_TIM0IRQ					0x02 /* Timer0 expiry */
#define INT_TIM1IRQ					0x04 /* Timer0 expiry */
#define INT_TIM2IRQ					0x08 /* Timer0 expiry */
#define INT_KBDIRQ					0x40 /* Keyboard interrupt (further key selection in keyboard module */
#define INT_PORIRQ					0x80 /* Supply failure on VCC. Also power-on is considered as an initial supply failure */ 

/* Keyboard Row Interrupt status. */
#define KINT_RSINT					0x01 /* Raw scan interrupt.Interrupt generated after keyboard scan, if the keyboard status has changed. */
#define KINT_RKLINT					0x02 /* Raw key lost interrupt indicates a lost key-code. */
#define KINT_REVTINT				0x04 /* Raw keyboard event interrupt.At least one key press or key release is in the keyboard event buffer */
#define KINT_RELINT					0x08 /* Raw event lost interrupt. More than 16 keyboard events have been detected and caused the event buffer to overflow */


/* Clock settings (CMD_CLKMODE). */
#define CLK_MODCTL					0x01 /* 00: SLEEP Mode, 01: Operation Mode .Writing to 00 forces the device to immediately enter sleep mode,*/

/* Clock settings (CMD_CLKEN). */
#define CLK_TIMEN					0x04 /* PWM Timer 0, 1, 2 clock enable */
#define CLK_KBDEN					0x01 /* Keyboard clock enable (enables/disables key scan) */

#define SCRIPT_1
//#define SCRIPT_2
//#define SCRIPT_3

static int lm8330_configure(struct lm8330_chip *lm)
{
	int keysize = (lm->size_x << 4) | lm->size_y;
	int clock = (CLK_TIMEN | CLK_KBDEN);
	int debounce = lm->debounce_time >> 2;
	int active = lm->active_time >> 2;

	/*
	 * Active time must be greater than the debounce time: if it's
	 * a close-run thing, give ourselves a 12ms buffer.
	 */
	if (debounce >= active)
		active = debounce + 3;

	lm8330_write(lm, 2, LM8330_CMD_AUTOSLP, 0x00);				/* disable autoslip */
	lm8330_write(lm, 2, LM8330_CMD_CLKMODE, 0x01);				/* set Operation mode */
	lm8330_write(lm, 2, LM8330_CMD_CLKEN, clock);				/* nable keyboard clock end timers */
	
	lm8330_write(lm, 2, LM8330_CMD_SET_KBDSETTLE, 0x80);		/* Set the keyscan settle time to 12 msec. */
	lm8330_write(lm, 2, LM8330_CMD_SET_KBDBOUNCE, debounce);	/* Set the keyscan debounce time */
	lm8330_write(lm, 2, LM8330_CMD_SET_KBDSIZE, keysize);		/* Set the keyscan matrix size to 5 rows x 10 columns */
	lm8330_write(lm, 3, LM8330_CMD_SET_KBDDEDCFG, 0xFF, 0xFF);	/* Confirm default value */


	lm8330_write(lm, 2,LM8330_CMD_SET_IOCFG, 0x11);				/* KPY10-> pwm, KPY11->IRQ */

	lm8330_write(lm, 3,LM8330_CMD_SET_IOPC0, 0xAA, 0xAA);		/* pull up resistor for row*/
	lm8330_write(lm, 3,LM8330_CMD_SET_IOPC1, 0x55, 0x55);		/* pulldown resistor for column 0-7*/
	lm8330_write(lm, 3,LM8330_CMD_SET_IOPC2, 0x5A, 0x05);		/* pulldown resistor for column 8-9*/

	lm8330_write(lm, 2,LM8330_CMD_RSTINTCLR, 0x01); 			/* Clear Power On interrupt */

	lm8330_write(lm, 2,LM8330_CMD_SET_KBDIC, 0x03); 			/* Clear all pending interrupts */

	lm8330_write(lm, 2,LM8330_CMD_SET_KBDMSK, 0x0B); 			/* Configure interrupt masking */

	/* PWM0 programming
	 *
	 */

	lm8330_write(lm, 2, LM8330_CMD_SET_TIMCFG0, 0x10);			/* Interrupt mask for PWM CYCIRQ0 */
	lm8330_write(lm, 2, LM8330_CMD_SET_PWMCFG0, 0x08);			/* CDIRQ disabled/masked, Pattern Generator disabled,	PWM disabled,PWM off-state is low */
	lm8330_write(lm, 2, LM8330_CMD_SET_TIMIC, 0x3F);			/* This register clears timer and pattern interrupts */
	lm8330_write(lm, 2, LM8330_CMD_SET_PWMWP, 0);				/* set PWM SCRIPT pointer buffer */
#ifdef SCRIPT_1
	// This script outputs a square wave with a duty cycle at 25%.  Why???????????????????????????????
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x00);		//0x40FF set duty cycle to 00%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x01, 0x7F);		//ramp 1 inc 126
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x01, 0x7F);		//ramp 1 inc 126
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x01, 0xFF);		//ramp 1 dec 126
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x01, 0xFF);		//ramp 1 dec 126
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x00, 0x00);		//goto start script
#endif
#ifdef SCRIPT_2
//This script outputs a square wave with a duty cycle that varies continuously from 0% to 25% (a minute or two) then outputs a square wave with a duty cycl 50%. Why??????????????????????????
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x7F);		//set duty cycle to 50%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x00);		//set duty cycle to 0%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0xFF);		//set duty cycle to 100%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x00, 0x00);		//go to start script
#endif
#ifdef SCRIPT_3
//This script outputs a square wave with a duty cycle that varies continuously from 0% to 25% (a minute or two), then stop. Why??????????????????????????
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x7F);		//set duty cycle to 50%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x00);		//set duty cycle to 0%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0xFF);		//set duty cycle to 100%
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xBF, 0xC1);		//loop
	lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x00, 0x00);		//go to start
#endif	
	
	/* Command repeated because some keypad configuration instruction puts it in sleep mode. ??????????????  */
	lm8330_write(lm, 2, LM8330_CMD_CLKMODE, 0x01); 				/*set Operation mode */

	lm8330_write(lm, 2, LM8330_CMD_SET_PWMCFG0, 0x06);			/* Start pwm script

	/*
	 * Not much we can do about errors at this point, so just hope
	 * for the best.
	 */

	return 0;
}

  • As shown on page 47, 0x7E is the low byte and 0x7F is the high byte, so you have to write the LSB first.

  • In my numerous attempts, I also tried to write LSB and then MSB, but it still didn't work. Then I noticed that the keyboard works if in the KBDDEDCFG register I write first MSB and then LSB unlike when reported on page 46 of the datasheet.

    Update

    I have retried to write correctly to register 0xFE and now it seems to work. Probably when I did the various tests also reversing the writing order there was some other problem. I will do other tests to confirm the resolution of the problem.
    Thanks.

    Last questions.
    With the following simple script

    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, DUTYCYCLE, 0x40);
    DUTYCYCLE = 0,..., 255

    I have performed various tests with different duty cycles and a frequency of 125Hz.

    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x00, 0x40);// set duty cycle to 0%
    ..
    ..
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x40, 0x40);// set duty cycle to 25%
    ..
    ..
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x7E, 0x40);// set duty cycle to 49,9%
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x7F, 0x40);// pwm out = 0
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x80, 0x40);// pwm out = 0
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0x81, 0x40);// set duty cycle to 50,1%
    ..
    ..
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xC0, 0x40);// set duty cycle to 75%
    ..
    ..
    lm8330_write(lm, 3, LM8330_CMD_SET_PWMCFG, 0xFF, 0x40);// set duty cycle to 100%


    Is it possible to program a higher frequency or is 125Hz the maximum possible frequency?
    And why are the values 0x7F and 0x80 not usable according to the tests shown?

  • Thanks Clemens.

    Hi Luigi,

    Glad to hear you were able to resolve your issue. Let us know if you run into any other issues.

    -Bobby