Jump to content
Sign in to follow this  
spirilis

My time with the Renesas RX

Recommended Posts

Got off my lazy ass and finally did a lesson - IRQs, particularly Pin Change interrupts.
 
Very simple as far as these things go IMO, you just enable the port as input, activate the input buffer, set the IRQMD settings for that port, then clear the interrupt/set its priority/enable it.
 
Not all ports have interrupt capability, they are denoted by IRQ0-15 in the pin designation.  The pin I chose is P44, also called AN4 and better known to us YRDKRX62N folks as the Analog Potentiometer.  Turning it up & down eventually triggers a high or low signal, so it's perfect for testing the various transitions.  It is IRQ12.
 
The code is pretty simple, the IRQ routine just flip-flops the status of PD6 (one of the LEDs) every time.
 
hardware_setup.c:

 

void HardwareSetup(void)

{
    SYSTEM.SCKCR.BIT.ICK = 0x00;  // ICLK = 96MHz
    SYSTEM.SCKCR.BIT.PCK = 0x01;  // PCLK = 48MHz
    SYSTEM.SCKCR.BIT.PSTOP1 = 1;
    SYSTEM.SCKCR.BIT.PSTOP0 = 1;


    PORTD.DDR.BIT.B6 = 1;
    PORTD.DR.BIT.B6 = 1;  // PD6 LED = OFF


    // AN4 potentiometer is also IRQ12
    PORT4.DDR.BIT.B4 = 0;  // Input
    PORT4.ICR.BIT.B4 = 1;  // Input buffer enabled
    ICU.IRQCR[12].BIT.IRQMD = 0x01;  // Falling Edge
    IR(ICU,IRQ12) = 0;  // Clear interrupt (always set IRQMD before doing this in case the IRQMD setting triggers a new interrupt)
    IPR(ICU,IRQ12) = 1; // Set interrupt priority (non-0)
    IEN(ICU,IRQ12) = 1; // Enable interrupt.
}


gpioint.c (main program--does nothing):
int main(void)
{
    // TODO: add application code here


    while (1) {
    // Literally nothing to do, it's all IRQ-driven.
        ;
    }
  return 0;
}[/code]


And interrupt_handlers.c:
[code]
#include "iodefine.h"
//;0x0130 IRQ12
void  INT_Excep_IRQ12(void){
    PORTD.DR.BIT.B6 ^= 1;
}

 

 


gpioint.c (main program--does nothing):

 

int main(void)
{
    // TODO: add application code here


    while (1) {
    // Literally nothing to do, it's all IRQ-driven.
        ;
    }
  return 0;
}

 


And interrupt_handlers.c:

 

#include "iodefine.h"
//;0x0130 IRQ12
void  INT_Excep_IRQ12(void){
    PORTD.DR.BIT.B6 ^= 1;
}

 

 
 

For the interrupt macros (IR, IPR, IEN) the hardware peripheral is just "ICU", Interrupt Control Unit, which is where the pin interrupts (IRQ0-15) live.  As opposed to TMR0, TMR1 and the like (which denote other HW peripherals on the MCU silicon)
 
Twisting the potentiometer back and forth causes the PD6 red LED to flip on or off each time the dial goes counterclockwise to register a LOW signal.

Share this post


Link to post
Share on other sites

Back at it -- this time with a very basic S12AD (12-bit ADC) example.

 

There are 3 ADCs -- S12AD, AD0 and AD1 (AD0/AD1 = 10-bit ADCs)

They all share the same analog input pins.  It's requested in the hardware user's guide that you shouldn't run the 12-bit and 10-bit ADCs simultaneously, although I'm guessing you can run both 10-bit ADCs simultaneously.

 

So the plan here is to read AN4 (potentiometer) and reflect the upper 8 bits of its result in PORTD, which should light up some LEDs in a rather random pattern (they're not dispersed evenly in a circle, but thrown about seemingly randomly--although not really, they correspond to other functions related to motor control, just doesn't follow the bit sequence in the port)

 

Hardware setup:

#include "iodefine.h"

void HardwareSetup(void)
{
	SYSTEM.SCKCR.BIT.ICK = 0x00;  // ICLK = 96MHz
	SYSTEM.SCKCR.BIT.PCK = 0x01;  // PCLK = 48MHz
	SYSTEM.SCKCR.BIT.PSTOP1 = 1;
	SYSTEM.SCKCR.BIT.PSTOP0 = 1;

	// AN4 read by ADC12, range used to select PD0-7 LEDs
	PORT4.DDR.BIT.B4 = 0;
	PORT4.ICR.BIT.B4 = 0;  // Input buffer not necessary

	PORTD.DDR.BYTE = 0xFF;
	PORTD.DR.BYTE = 0xFF;  // All PORTD LEDs off

	// Activate S12AD
	MSTP(S12AD) = 0;
	MSTP(AD0) = 1;  MSTP(AD1) = 1;  // Ensure ADC10 is disabled

	// Configure S12AD for single-scan on AN4
	S12AD.ADCSR.BIT.TRGE = 0;  // No external triggering
	S12AD.ADCSR.BIT.CKS = 0x03; // Clock = PCLK/1
	S12AD.ADCSR.BIT.ADIE = 0;  // Keep interrupts off
	S12AD.ADCSR.BIT.ADCS = 0;  // Single-cycle scan mode
	S12AD.ADANS.WORD = 1 << 4; // Enable AN4
	S12AD.ADADS.WORD = 0x00;   // No addition-mode please
	S12AD.ADCER.BIT.ACE = 1;  // Automatic clearing of ADDRx after read
	S12AD.ADCER.BIT.ADRFMT = 0; // Right-aligned data
	// S12AD is now prepared and ready to roll.  Set S12AD.ADCSR.BIT.ADST = 1 to initiate a conversion and wait for that bit to clear.
}

 

 

Main program (s12adc.c):
#include "iodefine.h"
#include <stdint.h>

int main(void)
{
    uint16_t adres;

    while (1) {
        S12AD.ADCSR.BIT.ADST = 1;
        while (S12AD.ADCSR.BIT.ADST)
            ;
        adres = S12AD.ADDR4;
        PORTD.DR.BYTE = (adres >> 4) & 0xFF;
    }
  return 0;
}

 

 

 

An interesting quirk is the S12.ADCER.BIT.ACE = 1 I did; this isn't too important for single-scan mode but it basically zeroes-out the ADDRx register after it's been read once.  It can be used to potentially validate whether a transfer completed correctly (e.g. DMA or DTC auto-xfer).  I enabled it for the hell of it since I only read the ADDR4 register once after a conversion and don't read from it again until another conversion is complete.

 

Sure enough, at full-on (clockwise) all the LEDs turn off (LEDs go between Vcc and the I/O pins), at full-off (counter-clockwise) they're all on.  Everything in between varies.  Looks like there are 12 LEDs in that ring, only 8 of them are twiddled here, I could in theory hunt down the other 4 and hit them with the lower 4 bits of the ADC value too.

 

Other exercises could include continuous-scan with IRQs enabled so the main function just runs an infinite loop, but I don't really care :smile:

Share this post


Link to post
Share on other sites

Ok, got my ADC10 example running.

 

The two 10-bit ADCs, AD0 and AD1, have their MUX's wired to 1/2 of the 8 available ADC ports--AD0 can read from AN0-AN3, AD1 from AN4-AN7.  The analog potentiometer on the board is wired to AN4 so I'm using AD1 for this example.

 

Very similar to the S12AD to be honest, just a few register name differences:

 

hardware_setup.c

void HardwareSetup(void)
{
    SYSTEM.SCKCR.BIT.ICK = 0x00;  // ICLK = 96MHz
    SYSTEM.SCKCR.BIT.PCK = 0x01;  // PCLK = 48MHz
    SYSTEM.SCKCR.BIT.PSTOP1 = 1;
    SYSTEM.SCKCR.BIT.PSTOP0 = 1;

    // AN4 read by ADC10, range used to select PD0-7 LEDs
    PORT4.DDR.BIT.B4 = 0;
    PORT4.ICR.BIT.B4 = 0;  // Input buffer not necessary

    PORTD.DDR.BYTE = 0xFF;
    PORTD.DR.BYTE = 0xFF;  // All PORTD LEDs off

    // Activate AD1 -- AN4-7 is on AD1, AN0-3 on AD0.
    MSTP(AD1) = 0;
    MSTP(S12AD) = 1;  MSTP(AD0) = 1;  // Ensure S12AD, AD0 is disabled

    // Configure AD1
    AD1.ADCSR.BIT.CH = 0x00;  // AN4
    AD1.ADCSR.BIT.ADIE = 0;   // No interrupts
    AD1.ADCR.BIT.CKS = 0x03;  // Clock = PCLK/1
    AD1.ADCR.BIT.MODE = 0x00; // Single-conversion mode
    AD1.ADCR.BIT.TRGS = 0x00; // Software-trigger
    AD1.ADDPR.BIT.DPSEL = 0;  // Right-aligned data
    // Keeping default value of AD1.ADSSTR (sampling time)
    AD1.ADDIAGR.BIT.DIAG = 0; // No diagnostics
    // Ready to go--set AD1.ADCSR.ADST=1 to start conversion and wait until it clears.
}

 

Main program (adc10.c):

#include "iodefine.h"
#include <stdint.h>

int main(void)
{
    uint16_t adres;

    while (1) {
        AD1.ADCSR.BIT.ADST = 1;
        while (AD1.ADCSR.BIT.ADST)
            ;
        adres = AD1.ADDRA;
        PORTD.DR.BYTE = (adres >> 4) & 0xFF;
    }
  return 0;
}

 

One minor bug in my code here is that I'm right-shifting the ADC value 4 bits, when it's only a 10-bit result, so as a result the LEDs corresponding to the upper 2 bits always read 0 (0=ON) so even at full-clockwise rotation 2 LEDs are lit.

 

Also something else I didn't notice yesterday but for some unknown reason noticed today, is "#include <stdint.h>" started throwing errors because the include path only contains the include to <compiler>\rx-elf\optlibinc and not the <compiler>\rx-elf\include (main include dir)... Funny enough the s12ad code started failing to compile too.  I added the correct path to both projects, under (right click on project in left pane)->Renesas Tool Settings->Compiler->Source, added it to the "Include file directories" pane. ("${TCINSTALL}\rx-elf\include")

 

Share this post


Link to post
Share on other sites

Strange, the 100pin version of this chip only has DA1 broken out, not DA0.  I'd think they'd expose DA0 instead of DA1 but alas... (referring to the DAC outputs)

 

 

edit: Yep, the Renesas reference library (RPDL) confirms this:

	/* Channel 0 selected? */
	if ((selection & PDL_DAC_10_CHANNEL_0) != 0x0u)
	{
#ifdef DEVICE_PACKAGE_LQFP_100
		/* No DA0 in 100-pin package */
		return false;
#else	
		/* Load the data register */
		DA.DADR0 = output_0;
#endif

Share this post


Link to post
Share on other sites

Hm, not understanding this.  DA.DADR1 = 0 results in 1.3V coming out of the P05/DA1 pin, DA.DADR1 = 1023 shows 3.27V like expected (seems to be Vcc), the rest of the values show something in between those.

 

I'd expect DA.DADR1=0 to show ~0V on DA1... No pullups are enabled, output buffer is disabled, input buffer disabled.  The section on the DAC doesn't mention anything about these anyhow. actually it does, it says set the DDR and ICR register bits for P05 to 0, which I have done.

 

Hmmm...

Share this post


Link to post
Share on other sites

Alright well, here's my attempt to use the DAC which seems to have failed;

 

hardware_setup.c

void HardwareSetup(void)
{
    SYSTEM.SCKCR.BIT.ICK = 0x00;  // ICLK = 96MHz
    SYSTEM.SCKCR.BIT.PCK = 0x01;  // PCLK = 48MHz
    SYSTEM.SCKCR.BIT.PSTOP1 = 1;
    SYSTEM.SCKCR.BIT.PSTOP0 = 1;

    // P05 is DA1, enable output
    PORT0.DDR.BIT.B5 = 0;  // Disable output buffers
    PORT0.ICR.BIT.B5 = 0;  // Ensure schmitt-trigger is off

    // Enable DA1 DAC output (DA0 is not pinned out on the LQFP-100 version of the RX62N)
    MSTP(DA) = 0;
    DA.DADPR.BIT.DPSEL = 0;  // Data loaded LSB-aligned
    DA.DACR.BIT.DAE = 1;     // Don't think this really matters here
    DA.DACR.BIT.DAOE1 = 1;   // DA1 enabled
}

 

 

Main program (dac.c):

#include "iodefine.h"
#include <stdint.h>

int main(void)
{
    DA.DADR1 = (uint16_t)768;  // 3/4ths of AVcc, should be around 2.4-2.5V
    while (1) {
        ;
    }
  return 0;
}

 

 

Technically the code does work, and different values assigned to DA.DADR1 do result in different voltages, it just seems the DAC is setting the floor at 1.3V instead of 0V like I'd expect.... the formula in the datasheet for determining the voltage output surely implies so.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×