C8051 and EFM8 MCUs contain a timer module called the Programmable Counter Array (PCA). This module consists of a timer and several Capture/Compare channels. Each of these channels can provide a PWM or Frequency output without any CPU intervention.
Each channel can be programmed individually, and channel outputs can be enabled through the crossbar. Once a channel is disabled on the crossbar, its pin reverts to the settings in the particular port register for that pin. However, enabling or disabling the individual channels through enabling/disabling their outputs on the crossbar can be problematic.
Firstly, Channels must be enabled or disabled in order - i.e., you cannot enable channel 2 without enabling channel 1. Likewise, you cannot disable channel 1 without first disabling channel 2. If channel 2 is the last enabled or disabled channel, it may be enabled or disabled freely, without interacting with previously enabled or disabled channels. It would be impossible to disable channel 0, for example, and leave channel 1 and channel 2 routed out.
Secondly, removing functions from the crossbar at run-time can cause other problems. For example, if other functions that have a lower priority than the PCA on the crossbar are enabled, enabling or disabling PCA channels will shift these functions to different pins. This can be partially solved by subsequently setting the skip bit for the disabled PCA channel pin, but, since this instruction cannot be executed simultaneously with the crossbar-removal instruction, there will be some amount of time where all crossbar functions after the PCA are shifted. This could cause glitches in these other functions.
// Disable PCA channel 2 output (assuming P0.2 for this example)
XBR1 = (XBR1 & ~XBR1_PCA0ME__FMASK) | XBR1_PCA0ME__CEX0_CEX1;
// Glitch! Next XBAR function after PCA channel 2 is now moved to P0.2!
// Skip PCA channel 2 pin (P0.2)
P0SKIP |= 0x02;
// Next XBAR function is now moved back from P0.2 to its previous location
The Alternative An alternative to the crossbar method is to exploit the behavior of the PCA channel settings. If a channel's Capture/Compare Mode register's TOG and PWM bits are cleared, the PCA channel output will be forced HIGH (100% duty cycle) immediately. Therefore, it is simple, if the channel needs to be disabled, but the output needs to be HIGH, to individually disable a channel by performing the following:
// Disable a channel with 'HIGH' output
PCA0CPMn &= ~(PCA0CPMn_PWM__BMASK | PCA0CPMn_TOG__BMASK);
However, it is slightly more complicated if the output must be driven LOW. You can exploit the fact that the pin will only be weakly pulled high when the pin is set to open-drain. In this case, a pull-down resistor will pull the channels pin to be low:
// Disable PWM on P0.1
// PULL-DOWN MUST BE INSTALLED
P0MDOUT &= ~P0MDOUT_B1__BMASK;
If the output needs to be driven LOW by the output driver (and not just pulled down by a resistor), the ECOM bit can be cleared whenever the output has been set to LOW. This will keep the output low. This does, however, require some delay in waiting for the channel output to move to the correct state - the other methods are instantaneous.
// Declare PCA_CH1 to be bit 1 of P0 register
sbit PCA_CH1 = P0^1;
// Wait for P0.1 (PCA channel 1) to transition LOW
while (P0_1 == 1);
// Clear the ECOM bit to keep the channel output LOW
PCA0CPM1 &= ~PCA0CPM1_ECOM__BMASK;
To facilitate a quicker change, the channel's match register can be updated to be closer to the current PCA counter value, so that the match occurs sooner.
Disabling Individual PCA Channel Outputs