0830> Today's task: Run the CDC example on my 5529 Jumppad board.
0856> The USBCDC_sendDataInBackground() call fails with USBCDC_BUS_NOT_AVAILABLE.
0956> Reading the Examples Guide document is good news and bad
news. Good news: I (might) know why the USB operations are failing;
the device is not being recognized by the host due to a missing and/or
unsigned INF descriptor. Bad news: Microsoft has made life with Win8
more difficult by requiring the INF be signed and certified, which is
impossible for experimental devices. The workaround is to enable
"unsafe drivers" during boot. Worst news: this must be manually
re-enabled for every boot.
1008> Now would be a very good time to make a backup of the VS12
machine. ETA: 50 minutes.
1052> Backup complete, SSD0240_02.
1119> Fortunately, TI provides a signed INF for the sample CDC
device. I open Control Panel > System > Change Settings > Hardware
System Properties > Hardware > Device Manager > Ports (COM & LPT).
Right-click "TI MSP430 USB" (with the yellow warning flag) and click
"Update Driver", select "Browse my computer" and navigate to the
USB_Config folder in the C0_SimpleSend project. Since this is a
signed INF, it should update without complaints or rebooting. If
all went well, the yellow warning flag should disappear and a new
COM port named "TI MSP430 USB" should appear. I connect to COM8
using Putty and I can now see the output from the 5529.
1131> Success! I now have serial output over USB/COM.
I need to see if I can send data to the 5529.
1205> Success! I now have two-way communication with the 5529 over
USB/COM. The difficult part was installing and configuring all the
software, actually sending and receiving data was trivial (apart from
misleading documentation). My changes are in bold.
Main.c:
#include <string.h>
#include "driverlib.h"
#include "USB_config/descriptors.h"
#include "USB_API/USB_Common/device.h"
#include "USB_API/USB_Common/usb.h" //USB-specific functions
#include "USB_API/USB_CDC_API/UsbCdc.h"
#include "USB_app/usbConstructs.h"
#include "hal.h"
int convertTimeBinToASCII(uint8_t* str, int strCt);
void initRTC(void);
volatile uint8_t hour = 4, min = 30, sec = 00; // Real-time clock (RTC) values. 4:30:00
volatile uint8_t bSendTimeToHost = FALSE; // RTC-->main(): "send the time over USB"
uint8_t timeStr[16]; // Stores the time as an ASCII string
uint8_t RcvChr; //Character received from host.
void main(void) {
int ChrCt;
WDT_A_hold(WDT_A_BASE); //Stop watchdog timer
// Minimum Vcore setting required for the USB API is PMM_CORE_LEVEL_2
PMM_setVCore(PMM_CORE_LEVEL_2);
USBHAL_initPorts(); // Config GPIOS for low-power (output low)
P1OUT= (P1OUT & ~0x01)|0x00; //LED1 off
P4OUT= (P4OUT & ~0x80)|0x00; //LED2 off
USBHAL_initClocks(8000000); // Config clocks. MCLK=SMCLK=FLL=8MHz; ACLK=REFO=32kHz
USB_setup(TRUE,TRUE); // Init USB & events; if a host is present, connect
initRTC(); // Start the real-time clock
__enable_interrupt(); // Enable interrupts globally
P4OUT= (P4OUT & ~0x80)|0x80; //GRN led on
while (1)
{
// Enter LPM0, which keeps the DCO/FLL active but shuts off the
// CPU. For USB, you can't go below LPM0!
__bis_SR_register(LPM0_bits + GIE);
// If USB is present, sent the time to the host. Flag is set every sec
if (bSendTimeToHost)
{
bSendTimeToHost = FALSE;
ChrCt= convertTimeBinToASCII(timeStr,sizeof(timeStr));
// This function begins the USB send operation, and immediately
// returns, while the sending happens in the background.
// Send timeStr, 9 bytes, to intf #0 (which is enumerated as a
// COM port). 1000 retries. (Retries will be attempted if the
// previous send hasn't completed yet). If the bus isn't present,
// it simply returns and does nothing.
if (USBCDC_sendDataInBackground(timeStr, ChrCt, CDC0_INTFNUM, 1000)) {
P1OUT= (P1OUT & ~0x01)|0x01; //ERROR: RED led on
_NOP(); // If it fails, it'll end up here. Could happen if
// the cable was detached after the connectionState()
// check, or if somehow the retries failed
} else {
P1OUT= (P1OUT & ~0x01)|0x00; //RED led off.
}
} else if(USBCDC_getBytesInUSBBuffer(CDC0_INTFNUM)>0) {
USBCDC_receiveDataInBuffer(&RcvChr,1,CDC0_INTFNUM);
}
} //while(1)
} //main()
void initRTC(void)
{
TA0CCR0 = 32768;
TA0CTL = TASSEL_1+MC_1+TACLR; // ACLK, count to CCR0 then roll, clear TAR
TA0CCTL0 = CCIE; // Gen int at rollover (TIMER0_A0 vector)
}
#if defined(__TI_COMPILER_VERSION__) || (__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR (void)
#elif defined(__GNUC__) && (__MSP430__)
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) TIMER0_A0_ISR (void)
#else
#error Compiler not found!
#endif
{
if (sec++ == 60)
{
sec = 0;
if (min++ == 60)
{
min = 0;
if (hour++ == 24)
{
hour = 0;
}
}
}
bSendTimeToHost = TRUE; // Time to update
__bic_SR_register_on_exit(LPM3_bits); // Exit LPM
}
int convertTwoDigBinToASCII(uint8_t bin, uint8_t* str) {
*str++= (bin/10)+'0';
*str++= (bin%10)+'0';
return(2);
}int convertTimeBinToASCII(uint8_t* str, int strCt) {
int nDst= 0;
nDst+= convertTwoDigBinToASCII(hour,&str[nDst]);
str[nDst++]= ':';
nDst+= convertTwoDigBinToASCII(min,&str[nDst]);
str[nDst++]= ':';
nDst+= convertTwoDigBinToASCII(sec,&str[nDst]);
str[nDst++]= '|';
//Echo received keystrokes
nDst+= convertTwoDigBinToASCII(RcvChr,&str[nDst]);
str[nDst++]= '\r';
str[nDst++]= '\n';
str[nDst]= 0;
return(nDst);
}
/*
* ======== UNMI_ISR ========
*/
#if defined(__TI_COMPILER_VERSION__) || (__IAR_SYSTEMS_ICC__)
#pragma vector = UNMI_VECTOR
__interrupt void UNMI_ISR (void)
#elif defined(__GNUC__) && (__MSP430__)
void __attribute__ ((interrupt(UNMI_VECTOR))) UNMI_ISR (void)
#else
#error Compiler not found!
#endif
{
switch (__even_in_range(SYSUNIV, SYSUNIV_BUSIFG )) {
case SYSUNIV_NONE:
__no_operation();
break;
case SYSUNIV_NMIIFG:
__no_operation();
break;
case SYSUNIV_OFIFG:
UCS_clearFaultFlag(UCS_XT2OFFG);
UCS_clearFaultFlag(UCS_DCOFFG);
SFR_clearInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT);
break;
case SYSUNIV_ACCVIFG:
__no_operation();
break;
case SYSUNIV_BUSIFG:
// If the CPU accesses USB memory while the USB module is
// suspended, a "bus error" can occur. This generates an NMI. If
// USB is automatically disconnecting in your software, set a
// breakpoint here and see if execution hits it. See the
// Programmer's Guide for more information.
SYSBERRIV = 0; // Clear bus error flag
USB_disable(); // Disable
}
}
It is interesting that the official TI reference code counts time
incorrectly. According to this, there are 61 seconds in every minute,
61 minutes in every hour, and 25 hours in every day.
Now that I have a KGR (Known Good Reference) for communicating with
the 5529, I need to extract the minimum required to enable USB/COM in
my own projects.
A New USB Project
1304> I am following the procedure from the "USB Programmer's Guide:
4.4 Adding USB into an Existing MSP430 Application." I am creating a new
workspace in EZ430\USB\Work\Motor\
And a new "Firmware" project:
NOTE: I used the wrong MCU number, it should be 5529 not 5229.
The USB support code needs to be copied into the project manually. The
reference files are found in the directory D:\CL\TI\CCS613\msp430\MSPWare_3_30_00_18\usblib430\MSP430_USB_Software\MSP430_USB_API
I set this as the current directory for D:. The C0_SimpleSend project
can be found in
examples\CDC_VirtualCOMport\C0_SimpleSend
I copy the USB_config directory from the C0_SimpleSend project.
This includes the KGR signed TI INF file and the USB Vendor/Product ID
codes. These will need to change if/when the product goes to
production, but re-using the signed INF saves a lot of
headaches. S:\Src\HQ\Dev\SB\Chip\EZ430\USB\Work\Motor\Firmware>xcopy /s D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\* USB_config\
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\descriptors.c
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\descriptors.h
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\msp430_cdc.cat
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\msp430_ti_signed.inf
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_config\UsbIsr.c
5 File(s) copied
Copy the usbConstructs.c and usbConstruct.h into the project source directory. S:\Src\HQ\Dev\SB\Chip\EZ430\USB\Work\Motor\Firmware>xcopy /s D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_app\* USB_app\
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_app\usbConstructs.c
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_app\usbConstructs.h
D:examples\CDC_virtualCOMport\C0_SimpleSend\USB_app\usbEventHandling.c
3 File(s) copied
Open the Firmware project properties and add the USB directories
to the include path. Add the root project directory (Firmware), and
driverlib\MSP430F5xx_6xx
1410> I should now be able to build the (empty) project. Now I
begin amalgamating the SimpleSend and JumpMtr projects into the new
project. I want to be able to control the speed of the motor using
Putty.
First step is to stub in the hardware initialization code. Most of
this code will be copied from the SimpleSend project.
Main_stubs:
#include "driverlib.h"
#include "descriptors.h"
#include "USB_API/USB_Common/device.h"
#include "USB_API/USB_Common/usb.h"
void __inline led_RedOff(void) { P1OUT&= ~0x01; }
void __inline led_RedOn(void) { P1OUT|= 0x01; }
void __inline led_GrnOff(void) { P4OUT&= ~0x80; }
void __inline led_GrnOn(void) { P4OUT|= 0x80; }
void initClocks(uint32_t hz);
void initPorts(void);
void initRTC(void);
void sendTime(void);
void recvText(void);
volatile uint8_t doSend= 0;
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
PMM_setVCore(PMM_CORE_LEVEL_2); //Minimum power for USB operation.
initPorts(); //Configure GPIO ports
led_RedOff(); //Turn error lamp off
led_GrnOff(); //Turn OK lamp off
initClocks(8000000); //MCLK=SMCLK=FLL=8MHz
USB_setup(TRUE,TRUE); //USB on
initRTC(); //Start real time clock
led_GrnOn(); //Tell the world we are open for business.
__enable_interrupt(); //Start listening
while(1) {
//Return to LPM0, minimum level for USB operations.
__bis_SR_register(LPM0_bits|GIE);
if(USBCDC_getBytesInUSBBuffer(CDC0_INTFNUM)>0)
recvText();
if(doSend)
sendTime();
}
return 0;
}
void initClocks(uint32_t hz) {
}
void initPorts(void) {
}
void initRTC(void) {
TA0CCR0= 32768; //Assuming 32KHz crystal on REF0.
TA0CTL= TASSEL_1|MC_1|TACLR;
TA0CCTL0= CCIE; //Interrupt on each rollover (1 second)
}
void sendTime(void) {
}
void recvText(void) {
}
The compiler complains that USBCDC_getBytesInUSBBuffer() is not
declared; I need to add the CDC include files:
Include CDC:
#include "USB_API/USB_CDC_API/UsbCdc.h"
#include "USB_app/usbConstructs.h"
The initClocks() code is copied from the example hal.c:
There is now a conflict between the RTC interrupt and MotorX, both
use TimerA. I change the RTC to use TimerB0. TODO: Use the real
hardware RTC.
RTC Using TimerB:
void initRTC(void) {
TB0CCR0= 32768; //Assuming 32KHz crystal on REF0.
TB0CTL= TBSSEL_1|MC_1|TBCLR;
TB0CCTL0= CCIE; //Interrupt on each rollover (1 second)
}
#pragma vector=TIMER0_B0_VECTOR
__interrupt void TIMER0_B0_ISR(void) {
...
}
Just to prove that both are working, I start up
the motor.
MotorOn:
void main(void) {
...
initRTC(); //Start real time clock
led_GrnOn(); //Tell the world we are open for business.
__enable_interrupt(); //Start listening
PWM_Config(1000);
while(1) {
...
}
}
Something is not right, it sounds like the motor is trying to run
too fast. The new project is configuring SMCLK for 8MHz, 8X faster
than JumpMtr. I bump the period in PWM_Config() from 1000 to 8000 and
the motor turns as expected.
The final step is to control the motor using commands from the
host.
I renamed PWM_Config() to mtrRun() to be consistent.
1612> Woot! Success! I am now able to drive my stepper motor using
keyboard commands from my Windows machine!
1639> It took a while to figure out why I could not transition
from Run to Step modes... I needed to set P1.4 back to normal GPIO
mode.
Step Working:
void mtrStop(void) {
TA0CTL= TASSEL_2|MC_0; //Stop TimerA
P4OUT&= ~0x08; //MTR_/RESET in reset
}
void mtrRun(unsigned int period) {
unsigned int pulse;
P8OUT= (P8OUT & ~0x06)|(StepMode<1);
P4OUT|= 0x08; //MTR_/RESET off
period= period*(8>>StepMode);
pulse= period/10;
TA0CCTL3= OUTMOD_7; //Toggle/Reset
TA0CTL= TASSEL_2 + MC_1; //Use SM clock (1us)
TA0CCR0= period-pulse; //Period (minimum 180)
TA0CCR3= pulse; //Pulse width (minimum 10)
P1DIR|= 0x10; //Output
P1SEL|= 0x10; //Select TA0.3
}
void mtrStep(void) {
TA0CTL= TASSEL_2|MC_0; //Stop TimerA
P1SEL&= ~0x10; //P1.4 is again a normal GPIO
P4OUT|= 0x08; //MTR_/RESET off
P8OUT= (P8OUT & ~0x06); //Step mode=FULL
P1OUT&= ~0x10; //P1.4=MTR_X_STEP low
__delay_cycles(10000); //This is just guess
P1OUT|= 0x10; //HIGH
__delay_cycles(10000);
P1OUT&= ~0x10; //LOW
}
void mtrReverse(void) {
P1OUT|= 0x40; //P1.6=MTR_X_DIR
}
void mtrForward(void) {
P1OUT&= ~0x40;
}
1640> Woot! Woot! I now have everything I need to control the
motors over the USB link.
The firmware was 11410 code bytes, using about 11K of 128K. Plenty
of room left for a G-Code interpreter. The entire Main.c:
TODO: Replace the pseudo-RTC TimerB with the real RTC.
I haven't seen anything from Bay Area Circuits about the CtrlMtr
boards. The order was placed 11:26am last Thursday with a 5-day turn,
and this is the fifth day. I guess the first day doesn't count, no
matter how early in the day the order was placed. With 2 days in
transit, it may be Monday before they arrive.
WebV7 (C)2018 nlited | Rendered by tikope in 71.172ms | 3.133.153.224