This doesn't answer your question, but might make the code a little easier for you to debug. The case statements are really long and may not be the best way to explain what you are doing with your outputs. I make no guarantees that the code is operational (I have not run it at all), but this should get you thinking about file size and readability.
Your singleminutes case statement has a truth table like this:
// | out
// in| 0 1 2 3 4
// ---------------
// 0 | 0 0 0 0 0
// 1 | 0 1 0 0 0
// 2 | 0 1 1 0 0
// 3 | 0 1 1 1 0
// 4 | 0 1 1 1 1
which might be better represented with output-centric code like this:
if (singleminutes >= 1)
PPEins = 1;
else
PPEins = 0;
if (singleminutes >= 2)
PPZwei = 1;
else
PPZwei = 0;
if (singleminutes >= 3)
PPDrei = 1;
else
PPDrei = 0;
if (singleminutes >= 4)
PPVier = 1;
else
PPVier = 0;
The nfminutes is a little more complicated, but here is the Truth Table:
// | MHUhr PMFuenf PMZehn PMViertel PMZwanzig PMVor PMNach PMHalb | |
// --|--------------------------------------------------------------|--------|-----
// 0 | 1 0 0 0 0 0 0 0 | 1000 0 | 000
// 1 | 0 1 0 0 0 0 1 0 | 0100 0 | 010
// 2 | 0 0 1 0 0 0 1 0 | 0010 0 | 010
// 3 | 0 0 0 1 0 0 1 0 | 0001 0 | 010
// 4 | 0 0 0 0 1 0 1 0 | 0000 1 | 010
// 5 | 0 1 0 0 0 1 0 1 | 0000 0 | 101
// 6 | 0 0 0 0 0 0 0 1 | 0000 0 | 001
// 7 | 0 1 0 0 0 0 1 1 | 0100 0 | 011
// 8 | 0 0 0 0 1 1 0 0 | 0000 1 | 100
// 9 | 0 0 0 1 0 1 0 0 | 0001 0 | 100
//10 | 0 0 1 0 0 1 0 0 | 0010 0 | 100
//11 | 0 1 0 0 0 1 0 0 | 0100 0 | 100
and again some output-centric code:
// MHUhr PMFuenf PMZehn PMViertel PMZwanzig
if( nfminutes == 0 )
MHUhr = 1;
else
MHUhr = 0;
if(( nfminutes == 1 ) || (nfminutes == 5) || (nfminutes == 7) || (nfminutes == 11))
PMFuenf = 1;
else
PMFuenf = 0;
if(( nfminutes == 2 ) || (nfminutes == 10) )
PMZehn = 1;
else
PMZehn = 0;
if(( nfminutes == 3 ) || (nfminutes == 9) )
PMViertel = 1;
else
PMViertel = 0;
if(( nfminutes == 4 ) || (nfminutes == 8) )
PMZwanzig = 1;
else
PMZwanzig = 0;
// PMVor PMNach PMHalb
if( ((nfminutes >= 1 ) && (nfminutes <= 4 )) || (nfminutes == 7))
PMNach = 1;
else
PMNach = 0;
if( (nfminutes >= 5) && (nfminutes <= 7 )
PMHalb = 1;
else
PMHalb = 0;
if(nfminutes >=8)
PMVor = 1;
else
PMVor = 0;
The code above might do well with some #defines too
#define UHR 0
#define PHUENF_NACH 1
#define ZEHN_NACH 2
...
if(nfminutes == UHR)
Again for hours. Truth Table:
| 12 1 2 3 4 5 6 7 8 9 10 11
//----|------------------------------------
// 0 | 1 0 0 0 0 0 0 0 0 0 0 0
// 1 | 0 1 0 0 0 0 0 0 0 0 0 0
// 2 | 0 0 1 0 0 0 0 0 0 0 0 0
// 3 | 0 0 0 1 0 0 0 0 0 0 0 0
// 4 | 0 0 0 0 1 0 0 0 0 0 0 0
// 5 | 0 0 0 0 0 1 0 0 0 0 0 0
// 6 | 0 0 0 0 0 0 1 0 0 0 0 0
// 7 | 0 0 0 0 0 0 0 1 0 0 0 0
// 8 | 0 0 0 0 0 0 0 0 1 0 0 0
// 9 | 0 0 0 0 0 0 0 0 0 1 0 0
// 10 | 0 0 0 0 0 0 0 0 0 0 1 0
// 11 | 0 0 0 0 0 0 0 0 0 0 0 1
and code. Slightly different structure with all outputs being cleared, then only the correct output turned on.
// one-hot, clear all will not cause a glitch
PHZwoelf = 0;
PHEins = 0;
PHZwei = 0;
PHDrei = 0;
PHVier = 0;
PHFuenf = 0;
PHSechs = 0;
PHSieben = 0;
PHAcht = 0;
PHNeun = 0;
PHZehn = 0;
PHElf = 0;
if( hours == 0 )
PHZwoelf = 1;
if( hours == 1 )
PHEins = 1;
if( hours == 2 )
PHZwei = 1;
if( hours == 3 )
PHDrei = 1;
if( hours == 4 )
PHVier = 1;
if( hours == 5 )
PHFuenf = 1;
if( hours == 6 )
PHSechs = 1;
if( hours == 7 )
PHSieben = 1;
if( hours == 8 )
PHAcht = 1;
if( hours == 9 )
PHNeun = 1;
if( hours == 10 )
PHZehn = 1;
if( hours == 11 )
PHElf = 1;
All this also allows you to do your input calculations together before your case statements.
// update single minutes
int singleminutes = (int) (unbcd(tm.min)%5); // 1, 2, 3, 4
// update 5 minutes
int nfminutes = (int) (unbcd(tm.min)/5); // Fuenf Nach, Zehn Nach, ...
// update hours
int hours = (int) (unbcd(tm.hour)%12); // 12, 1, 2, 3, 4...
if(nfminutes>=5) hours++; // 7:25 = Fuenf Vor Halb Acht (8)
You should check out example 7-2 in the pic24E family reference manual (FRM) titled "Code Example for Using PLL with 7.37 MHz Internal FRC":
// Select Internal FRC at POR
_FOSCSEL(FNOSC_FRC & IESO_OFF);
// Enable Clock Switching and Configure Primary Oscillator in XT mode
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_NONE);
int main()
{
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD=63; // M=65
CLKDIVbits.PLLPOST=0; // N2=2
CLKDIVbits.PLLPRE=0; // N1=2
// Initiate Clock Switch to FRC oscillator with PLL (NOSC=0b001)
__builtin_write_OSCCONH(0x01);
__builtin_write_OSCCONL(OSCCON | 0x01);
// Wait for Clock switch to occur
while (OSCCONbits.COSC!= 0b001);
// Wait for PLL to lock
while (OSCCONbits.LOCK!= 1);
}
It looks like the critical step you're missing is __builtin_write_OSCCONL(OSCCON | 0x01);
Also, looking at the math: 7.37*(76/(2*2)) == 140.03MHz which is slightly outside the allowed range, assuming that 140MHz is actually the maximum range (don't ask me why but for some reason it seems like it may be 120MHz).
If this still doesn't work then perhaps there's just an issue with your power supply. The internal FRC oscillator is unstable under temperature and voltage stress, so perhaps you should check to see if you have too much noise. This would make the FRC wonky as well as the VCO used in the PLL, preventing a lock.
If you look at table 30-18 in the pic24EP128MC206 datasheet, it tells you that over the temperature and voltage range you have about a ±1% for some models and ±2% for others. Figure 31-9 shows the variation with a stable voltage over a temperature range. There doesn't appear to be an analysis of voltage variation at a stable temperature.
If you're trying to get a stable run at a high frequency I would just grab a crystal.
EDIT (from comment):
So from the sounds of your other posts about your conditions it sounds like you should look elsewhere for a problem. What's the frequency of the ripple? Did you size the internal Vreg capacitor properly? It sounds like since this is a locking issue and not a setting issue you're having some other problems with the board that aren't related to your code.
EDIT:
Glad this turned out to be the right answer! Good luck debugging the rest of the board!
Best Answer
For both sending and receiving, you need to set up a timer interrupt.
One bit time for 115,200 baud is:
$${1,000,000 \over 115,200} = 8.68\space µs$$
If you are running with a peripheral clock of 48 MHz, then this corresponds to a period of 20.8 ns, or 0.0208 µs:
$${1,000,000 \over 48,000,000} = 0.0208\space µs$$
The number of counts for the timer is then:
$${8.68 \over 0.0208} = 417$$
So transmitting is easy, just set up a counter, and on each interrupt output the start bit, followed by 8 data bits, followed by a stop bit which lasts two timer intervals.
Receiving is a little trickier. To catch the falling edge of the RX lead, indicating the beginning of the start bit, you could use an Change Notification as you mentioned in a comment. Or, if you have an interrupt pin (INTx) unused, you could use that instead.
When the Change Notification interrupt comes in, you then want to reset your set your timer for one half the period, i.e. 4.34 µs, or 208 counts. Then, when the timer interrupt comes in, you should be in the middle of the start bit. If it has gone back high, then this was just noise. Otherwise change the timer back to one bit time (8.68 µs), and on each timer interrupt you will be sampling the middle of each data bit.
Ideally, you would like to scan each bit more than once. So if you set your initial timer after the Change Notification interrupt for 3.34 µs (160 counts) instead of 4.34 µs, you will be sampling the middle of the start bit minus one µs. Then wait 1 µs and sample again (you are now in the middle), and then once more (1 µs past the middle). You now have three samples to look at, so you can take the majority value. Wait 6.68 µs (321 counts) this time and repeat for each data bit.
Hardware UART's typically sample 16 times for each bit time.
You might want to get the simpler version working first (scanning just once in the middle).
EDIT:
I don't believe your setup is correct. The second line of your listing assumes your peripheral clock is 48 MHz, but looking at your clock setup, I think you are only running at 8 MHz.
Without an external crystal, the only way to get above frequencies of 8 MHz is to use the PLL. However you have the oscillator selection set to just FRC, which uses the internal 8 MHz oscillator directly. To get 48 MHz, you would need something like the following settings:
In Figure 8-1, page 141 of the datasheet, it says Fin must between 4 and 5 MHz. That means you must divide the internal RC clock by 2 (FPLLIDIV). Then you multiply it by 24 (FPLLMUL) and divide it by 2 (FPLLODIV) to get 48 MHz.
I don't know what the SYS_FREQ #define is being used for. Seems it should match GetSystemClock.
You didn't say which PIC32MX processor you are using. If it is one in the PIC32MX575/675/695/775/795 family, the internal FRC clock is only accurate to ±2% (Table 31-19, page 376). This may not be good enough for doing bit-banged UART at 115,200 baud -- generally baud rates need to be within 1.5%. You really should be using a 8 MHz crystal (with the clock settings updated once again) instead of the internal FRC clock.