Bit Operation of MCU C Language

Publisher:不羁少年Latest update time:2019-11-13 Source: 51heiKeywords:MCU Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Since the PIC processor is the most efficient at bit operations, putting some BOOL variables in one memory bit can achieve both fast operation speed and maximum space saving.
There are many options for bit operations in C.

**********************************************

For example: char x;x=x|0B00001000; /*Set the 4th bit of X to 1. */

char x;x=x&0B11011111; /*Clear the 5th bit of X to 0. */
The above formula is:
#define bitset(var,bitno)(var |=1<#define bitclr(var,bitno)(var &=~(1<Then the above operation is: char x;bitset(x,4)
char x;bitclr(x,5)
*************************************************
However, the above method has a disadvantage, that is, the meaning of each bit is not intuitive. It is better to be able to intuitively see the meaning of each bit in the code, so as to improve programming efficiency and avoid errors.
If we want to use bits 0-2 of X to represent the BOOL values ​​of temperature, voltage, and current respectively, we can do it as follows:
unsigned char x @ 0x20; /*Define the X variable to a fixed memory like in assembly. */
bit temperature@ (unsigned)&x*8+0; /*Temperature*/
bit voltage@ (unsigned)&x*8+1; /*Voltage*/
bit current@ (unsigned)&x*8+2; /*Current*/
After this definition, the bits of X have a vivid name, and are no longer the boring numbers 1, 2, 3, 4, etc.
X can be modified globally or operated on each bit:
char=255;
temperature=0;
if(voltage)......
******************************************************************
Another method is to define it with C struct structure:
such as:
struct cypok{
                     temperature:1; /*temperature*/
                     voltage:1; /*voltage*/
                     current:1; /*current*/
none:4;
           }x @ 0x20;
In this way, you can use
x.temperature=0;
if(x.current)....
and other operations.
**************************************************************
The above method is very effective in some simple designs, but it is more difficult for complex designs. For example, in multi-channel industrial control. The front end needs to collect multiple signals of multiple channels separately, and then set the control of multiple outputs of multiple channels. For example: there are 2-channel control, and the front-end signals of each channel have temperature, voltage, and current. The back-end controls motors, speakers, relays, and LEDs. If it is implemented in assembly, it will be a headache. It is very easy to implement in C. It also involves some memory management of C (in fact, the biggest advantage of C is memory management). Use the following structure:
union cypok{
                 struct out{
                                 motor:1; /*motor*/
                                 relay:1; /*relay*/
                                 speaker:1; /*speaker*/
                                 led1:1; /*indicator*/
                                 led2:1; /*indicator*/
                              }out;
struct in{
                                none:5;
                                temperature:1; /*temperature*/
                                voltage:1; /*voltage*/ current:1; /*
                                current*/
                               }in;
                 char x;
};
union cypok an1;
union cypok an2;
What are the advantages of the above structure? Let me tell you:
It subdivides the signal paths an1 and an2;
It subdivides the type of signal in each path (is it the front-end signal in or the back-end signal out):
an1.in;
an1.out; 
an2.in;
an2.out;
and then the specific meaning of each signal is subdivided, such as:
an1.in.temperature;
an1.out.motor;
an2.in.voltage;
an2.out.led2; and so on
. This structure is very intuitive to represent 2 signals in 2 memories. And it can be expanded very conveniently.
If you want to add more signals, you only need to add:
union cypok an3;
union cypok an4;
. . . . . . . . . . . . . . . . . . . . .
From the above, we can see the great benefits of using C.
PICC daily post. (How to switch from assembly to PICC)  
  
I am not talented, but I would like to share my experience with you.
Let's talk about how to switch from assembly to PICC.
Because there are many cracked versions of HIDE-TECH PICC, HIDE PICC has more users than other PICCs, although its compilation efficiency is not the best. The best is CCS, but there is no cracked version. . . However, using HIDE PICC to carefully arrange functions can also achieve high compilation efficiency. The human brain is still the best.


Of course, you need to have a basic knowledge of C language. PICC does not support C++, so friends who are used to C++ need to read books on C language.


The header file of the C code must have
#include.
It is a collection of many header files. The C compiler automatically embeds other corresponding header files in pic.h according to your chip.


This is better than compilation.


The loaded header file actually declares the registers and some functions of the chip.
Here is a snippet:
static volatile unsigned char TMR0 @ 0x01;
static volatile unsigned char PCL @ 0x02;
static volatile unsigned char STATUS @ 0x03;
It can be seen that it is similar to the definition of registers in the assembly header file. As follows:
TMR0 EQU 0X01;
PCL EQU 0X02;
STATUS EQU 0X03;
All of them define boring addresses as names that are recognized by everyone.


1: How to add a value?
For example, to add a value to TMR0:
In assembly: MOVLW 200;
MOVWF TMR0; Of course, you must ensure that the current page is 0, otherwise there will be an error.
C language: TMR0=200; // No error will occur regardless of the page.
It can be seen that C is very straightforward. And the biggest advantage is that when operating a register, you don't have to consider the page issue. Everything is done automatically by C.


2. How to do bit manipulation?
Bit manipulation in assembly is very easy. It is even simpler in C.
The header file of C has defined names for each bit of all registers that may need bit manipulation:
for example, each I/O port of PORTA is defined as: RA0, RA1, RA2... RA7.
Each bit of OPTION is defined as: PS0, PS1, PS2, PSA, T0SE, T0CS, INTEDG, RBPU.
You can directly perform operations and add values ​​to them.
For example:
RA0=0;
RA2=1;
in assembly it is:
BCF PORTA, 0;
BSF PORTA, 2;
it can be seen that the two are similar, but C does not need to consider the page problem.


3. Memory allocation problem:
Defining a memory in assembly is a very careful problem. You need to consider too many issues. If you are not careful, you will make mistakes. For example, 16-bit operations, etc. You don't need to consider too much when using C.
Here is an example:
16-bit division (C code):
INT X=5000;
INT Y=1000;
INT Z=X/Y;
but in assembly, you need to spend too much effort.
Here is a small C code to control the flashing of an LED with RA0:
#include
void main(){
int x;  
CMCON=0B111; file://Turn off the A port comparator, if there is a comparator function.
ADCON1=0B110; file://Turn off the A/D function, if there is an A/D function.
TRISA=0; file://A port is all output.
loop:RA0=! RA0;           
for(x=60000;--x;){;} file://Delay
goto loop;
}
Talk about RA0=! The meaning of RA0: PIC's operations on the PORT register are first read-modify-write.

The meaning of the above sentence is that the program first reads RA0, then inverts it, and finally rewrites the calculated value into RA0, which realizes the flashing function.

(A little experience) How to effectively control the LED flashing in real time.  
  
In many designs, it is necessary to have a wonderful and practical LED flashing to indicate whether the device is working properly and the working status.


In some designs where real-time requirements are not high, delays can be inserted to control LED flashing.


Its shortcomings are obvious: 1: The LED flashing mode is slow to respond. 2: Other work cannot be done during the delay process (except interruption), which wastes resources. 3: The code is long, and there are only a few instructions to actually control the LED, and other delay codes occupy 99% of the space.


If you use TMR1 or TMR2 as a clock, the above shortcomings can be avoided, allowing you to free up a lot of time to do more effective work.


The following is an example of C code using TMR1 as the clock (RB1, RB2, RB3 control LEDs):
void set_tmr1(){
TMR1L=0xdc;
TMR1H=0xb; /*Set initial value to 3036*/
T1CON=0B10001; /*Set TMR1 to overflow once every 0.125s*/
}
void interrupt time(){
if(TMR1IF){
T1CON=0B10000; /*Turn off TMR1*/ 
TMR1L=0xdc;                  
TMR1H=0xb; /*Set initial value for TMR1
T1CON=0B10001; /*Reset the division ratio and turn on TMR1*/ 
if(s++>8){ /*Clear every S*/
s=0;
if(ss++>60)/*Clear every minute*/
ss=0;
}
TMR1IF=0;
return;
}
}unsigned char s; /*Add 1 every 0.125S*/
unsigned char ss; /*Add 1 every 1 second*/
void main(){
set_tmr1();
........; /*Set I/O port, open TMR1 interrupt*/
while(1){
if(...) /*Judge the flashing mode statement, the same below*/
RB1=(bit)(s>4); /*Flash once every 1s, duty cycle 50% (adjustment> the following value can be changed)*/
if(...)
RB2=(bit)(!ss); /*Flash once every 1 minute, on for 1 second, off for 59 seconds*/
if(...)
RB3=(bit)(s==0 || s==2 || s== 4 || s== 6); /*Flash once every 0.25S*/
.........; /*Other work*/

}
Such a framework is very effective for programs based on software queries that require high real-time performance.
Use constant pointers in PICC.  
Constant pointers are very flexible to use and can bring a lot of convenience to programming.
I have tested that PICC also supports constant pointers and will automatically page, which is really a great thing.
Define a constant pointer to 8-bit RAM data (starting at 0x00):
#define DBYTE ((unsigned char volatile *) 0)
Define a constant pointer to 16-bit RAM data (starting at 0x00):
#define CWORD ((unsigned int volatile *) 0)
The 0 in ((unsigned char volatile *) 0) indicates the starting address of the RAM area, which can be modified flexibly.

The x in DBYTE[x] indicates the offset.

Here is a piece of code 1:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void main(void){
long cc=0x89abcdef;
a1=DBYTE[0x24];
a2=DBYTE[0x25];
a3=DBYTE[0x26];
a4=DBYTE[0x27];
while(1);
}

2:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
3:
char a1,a2,a3,a4;
#define DBYTE ((unsigned char volatile *) 0)
void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
bank1 static long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
A little application of BOOL quantity.  
  
  /*Bit type variables can only be global or static,
Sometimes, in practical applications, we need to change the value of a certain "bit" variable
and ensure the independence of this function. In this case, we have to
make this function a parameterized function, but bit variables cannot be used as parameters. What
should we do? Fortunately, there are bit segments.
Take a look: */
/************************************************/ 
union FLAG
{
unsigned char BYTE;
struct 
{
  unsigned char b0:1;
  unsigned char b1:1;
  unsigned char b2:1;
  unsigned char b3:1;
  unsigned char b4:1;
  unsigned char b5:1;
  unsigned char b6:1;
  unsigned char b7:1;  
}bool;
};
/************************************************/
union FLAG mode;
#define auto_bit mode.bool.b0
#define cool_bit mode.bool.b1
#define dar_bit mode.bool.b2
#define fan_bit mode.bool.b3
#define heat_bit mode.bool.b4
#define swing_bit mode.bool.b5
#define bed_bit mode.bool.b6
#define time_bit mode.bool.b7
/************************************************/
void mode_task(in_mode)
union FLAG *in_mode;
{
in_mode -> bool.b0=1;
in_mode -> bool.b5=1;
/*You can also write like this
in_mode -> BYTE|=0x21;*/
}
/************************************************/
void main(void)
{
  mode.BYTE=0X00;
  while(1)
  {
   mode_task(&mode);
  }
}
/********************************************/
How cool it is to write like this!
This involves structures, unions, bit segments, and pointers; you must first understand the basic concepts!

   Use PICC to write efficient bit shift operations.  
  
  Bit shift operations are required in many analog serial communications.
Taking the read byte of 1-W bus as an example, the original code is:
unsigned char read_byte(void)
{
unsigned char i;
unsigned char value = 0;
for (i = 0; i < 8; i++)
{
  if(read_bit()) value| = 0 x 01<  // reads byte in, one byte at a time and then
  // shifts it left
  delay(10); // wait for rest of timeslot
}
return(value);
}
Although it can be used, the execution efficiency after compilation is not high. This is also the reason why many friends think that C cannot be compared with assembly.

[1] [2]
Keywords:MCU Reference address:Bit Operation of MCU C Language

Previous article:Matrix keyboard scanning of PIC microcontroller development board (if()else nested method)
Next article:PIC microcontroller controls the motor to start, rotate, and stop C language program

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号