I was actually tempted to dig into the ADC functionality, however serial communications seemed to be a higher priority thing to learn.
Why?
Let's face it : we (normally) do not have a screen where we can output some debug information, our best bet with microcontrollers is the UART and use a terminal program on a PC to visualize some data.
As I already anticipated in a previous post, the serial interface is handled quite at "high level" with the C2000, in a similar way you would deal with it on a PC.
First of all , in the C2000 the uart is referred a SCI (Serial Communication Interface) and since there is a single module "A", it normally becomes SCI-A or SCIA.
I am starting to look around the SCIA using the demo program tha comes with the c2000 Launchpad, unfortunately that one does not cover the RX part, and therefore all the interrupt handling is not covered either.
We will need to look somewhere else for that.
One interesting thing to notice is that the uart has double buffered RX and TX functions which is kind of neat.
Unfortunately it looks like that a single hardware uart is supported in the piccolo (some devices may support SCI-B), which is a pity since at least two serial ports are always handy, typically you would use one to communicate with a device and the other one to debug whatever you need with a PC.
At least this is what I normally do.
It should be possible and quite easy to setup a software uart if needed, I hope.
Another thing I noticed is that it looks like there is no hardware flow control functionality provided, no big deal probably as a couple of gpio lines could be eventually used for that, after all that's the beauty of dealing with an mcu.
So, this said, we expect to have only two pins to deal with : RX and TX.
In fact they are accessible from the Launchpad expansion headers
(from the C2000 Launchpad pdf, by TI)
SCIRXDA (RX) is available on J1.3 (mode 1), J2.2 (mode 2), J2.9 (mode 2)
SCITXDA (TX) on J1.4 (mode 1), J1.7 (mode 2), J2.3 (mode 2)
J1.4 (mode 1) means header J1, pin 4 and MUX mode set to 1
Now, the pdf that came with the Launchpad explains that the board has an FTDI interface to which, by default, the serial communications are redirected.
That makes them available through the USB port.
The Switch S4 connects and disconnects the uart from the ftdi, so, normally, if you want to use the ttl uart signals (3.3V tolerant) you are supposed to switch S4 so that they are isolated from the USB.
But then I am wondering :
According to the Launchpad schematics, S4 connects and disconnects J1.3 and J1.4 (GPIO28 and GPIO29), which would mean that if we use another combination (i.e. the J2.2/3 option) normally we would be isolated from the USB.
That's something I will probably test later.
Let's start simple and imagine we want to connect the SCIA to the usb/ftdi, then we will have the S4 switch set to ON and configure properly the GPIO28 and 29 pin on the mcu.
The demo program has it all in this case :
GPIO_setPullUp(myGpio, GPIO_Number_28, GPIO_PullUp_Enable);
GPIO_setPullUp(myGpio, GPIO_Number_29, GPIO_PullUp_Disable);
GPIO_setQualification(myGpio, GPIO_Number_28, GPIO_Qual_ASync);
GPIO_setMode(myGpio, GPIO_Number_28, GPIO_28_Mode_SCIRXDA);
GPIO_setMode(myGpio, GPIO_Number_29, GPIO_29_Mode_SCITXDA);
The RX is going to be configured as input (implicitly, that's done automatically when setting the pin to uart mode).
To avoid floating signals the pullup resistor is enabled.
TX is obviously output, therefore pull resistors are not used.
The
setQualification part is related to synchronizing and qualifying the signal with the internal clock.
I will not enter too much in the details here, but let's just say that if a signal uses a clock that is not defined internally (this is the case for the RX signal whose clock depends on the other device on the communication line), then it needs to be synchronized with the internal clock to detect the gap with it.
A number of samples (configurable) is taken and a high frequency clock (SYSCLK or a divided value) is used to assess the gap.
The process is described in the 2802X_system manual.
The final two lines set the MUX mode for GPIO28 and 29 pins, the GPIO_28_Mode_SCIRXDA and GPIO_29_Mode_SCITXDA are two enum constants (value = 1).
If instead we would have preferred to use the J2 option (GPIO19 = RX, GPIO12 =TX) the needed code would have been :
GPIO_setPullUp(myGpio, GPIO_Number_19, GPIO_PullUp_Enable);
GPIO_setPullUp(myGpio, GPIO_Number_12, GPIO_PullUp_Disable);
GPIO_setQualification(myGpio, GPIO_Number_19, GPIO_Qual_ASync);
GPIO_setMode(myGpio, GPIO_Number_19, GPIO_19_Mode_SCIRXDA);
GPIO_setMode(myGpio, GPIO_Number_12, GPIO_12_Mode_SCITXDA);
And presumably the S4 switch would not affect at all, however, before connecting a ttl serial device to them, I would make sure that GPIO28 and 29 are NOT configured as SCIA at the same time (!!).
Now that we have the pin configured properly, let's take a look to the function that configures the SCIA peripheral (still from the demo software)
mySci = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj));
This line initializes a SCI handler, which will be used later.
void scia_init()
{
CLK_enableSciaClock(myClk);
We also initialized and configured properly the clock (
check this other post for details) , now we are using the myClk handler to enable the clock for the SCI peripheral
SCI_disableParity(mySci);
SCI_setNumStopBits(mySci, SCI_NumStopBits_One);
SCI_setCharLength(mySci, SCI_CharLength_8_Bits);
SCI_enableTx(mySci);
SCI_enableRx(mySci);
Ok, this part is quite obvious, we are using the classic 8-N-1 setup and we want both RX and TX channels enabled.
SCI_enableTxInt(mySci);
SCI_enableRxInt(mySci);
Also we think it is cool to have interrupts on both RX and TX buffers, then we want to set the Baud Rate.
At this point, you realize that the way these libraries have been created allow you to perform all the needed configuration without knowing much about the internals of the device.
... but some details cannot hurt.
The base clock of the SCI is the low speed clock (LSPCLK), then a 16 bit register (BRR) is provided to configure a divider.
That allows up to 65535 different BR configurations, the formula is the following :
By default the LSPCLK is set to SYSCLK / 4 ( 60MHz/4 in our case = 15MHz)
According to the formula, assuming BRR = 15
-> BR = 15.000.000 / (15 +1)*8 = 15.000.000 /128 = 117.2K which is not exactly 115.2K but close enough.
Using BRR = 16 we would have 15.000.000 / 136 = 110.3K and using BRR = 14 : 125K
So, finally, BRR = 15 is the best option
// Configured for 115.2kbps
#if (CPU_FRQ_60MHZ)
SCI_setBaudRate(mySci, SCI_BaudRate_115_2_kBaud); // value is 15
#elif (CPU_FRQ_50MHZ)
SCI_setBaudRate(mySci, (SCI_BaudRate_e)13);
#elif (CPU_FRQ_40MHZ)
SCI_setBaudRate(mySci, (SCI_BaudRate_e)10);
#endif
Obviously we need a different divider if the CPU is not set to 60MHz, but hey, you have the formula, you can do the math :)
The following code enables the fifo buffers for both the RX and TX channels, then it enables the TX fifo interrupt, but not the RX one.
That's because, I suppose, the sample program is not meant to receive any data, in a real world application we would probably need that one too.
Despite the fact that the TX interrupt is enabled, there is no interrupt handler function in the demo program.
I assume the interrupt is used internally by the library in order to manage properly the output buffer.
SCI_enableFifoEnh(mySci);
SCI_resetTxFifo(mySci);
SCI_clearTxFifoInt(mySci);
SCI_resetChannels(mySci);
SCI_setTxFifoIntLevel(mySci, SCI_FifoLevel_Empty);
SCI_resetRxFifo(mySci);
SCI_clearRxFifoInt(mySci);
SCI_setRxFifoIntLevel(mySci, SCI_FifoLevel_4_Words);
Finally, after clearing the interrupts, the buffers etc, a priority is set and the module enabled
SCI_setPriority(mySci, SCI_Priority_FreeRun);
SCI_enable(mySci);
return;
}
checking in the header file it looks that the following priorities are available :
SCI_Priority_Immediate=(0 << 3), //!< Denotes an immediate stop
SCI_Priority_FreeRun=(1 << 3), //!< Denotes free running
SCI_Priority_AfterRxRxSeq=(2 << 3) //!< Denotes that a stop after the current receive/transmit sequence
That completes the hardware configuration, on the software side, the port is dealt with similarly to a stream (or file) :
status = add_device("scia", _SSA, SCI_open, SCI_close, SCI_read, SCI_write, SCI_lseek, SCI_unlink, SCI_rename);
fid = fopen("scia","w");
freopen("scia:", "w", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
First a "device" named
scia is added, then it is opened for output and finally redirected to the standard output device (stdout).
From now on, each write to stdout will actually be directed to the uart :
putchar(0x1B);
This was a very high level first look into the serial communications of the C2000, will come back in the future with some more details and some experiments.