Tag Archive for microcontroller

Analog to Digital Signal Rectification

facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

Hello,

Often in my projects i need to detect the signal level and then feed it to an A/D Converter. Since Digital circuitry operate on a single rail power supply usually from 1.8V to 5V you cannot feed the analog signal to the ADC’s input referenced to digital ground because the negative polarity will be ignored or worse, could damage the ADC.

One way is to use an analog rectifier and convert this output to digital. This requires some extra components though and depending on maximum frequency of the signal can be expensive.

In audio domain I usually rectify it in software and I provide a DC offset on the ADC input in order to keep both positive and negative side of the signal within the ADC’s input range.

To have a symmetric voltage swing the offset is VDD/2. This of course gives a first limitation of peak input voltage of VDD/2 but it can be easily resolved by attenuating the signal first and keeping the attenuation level in mind in software side. This way we can scale the signal to fit our input requirements.

Digital Rectifier ADC Software signal rectifier

 

 

In the picture above (a) shows you the signal with a VDD/2 offset presented at the input of our A/D converter. We will sample this signal in time as shown on the x axis. and the black lines represent the result after sampling

(b) shows how to reconstruct the analog signal. You can see that for values above the VDD/2 line the green part exists in our sample value from (a) and for samples below VDD/2 line we need to add the green part.

Looking at our analog input signal and samples in (a) and looking at the green parts in (b) we can say that the signal value v(t) for each sample s[n] is:

v(t1) = s[1]-VDD/2

v(t2) = s[2]-VDD/2

v(t3) = s[3]-VDD/2

v(t4) = s[4]-VDD/2

v(t5) = s[5]-VDD/2

…..

What the above list describes are actually the green parts in (b). The problem that remains is that v(t3) and v(t4) (corresponding for the negative voltage swing in the input signal) are negative because sample 3 and 4 are lower in value than VDD/2.

What we did so far is eliminate the input offset and now in order to rectify it we need to make all the negative values positive. This is simply done this way:

v(t1) = s[1]-VDD/2

v(t2) = s[2]-VDD/2

v(t3) = VDD/2-s[3]

v(t4) = VDD/2-s[4]

v(t5) = s[5]-VDD/2

…..

Now the signal is rectified. v(t3) and v(t4) are positive and the list above describes now figure (c), the rectified output.

There are a couple of drawbacks in using this technique. First is the stability of the offset VDD/2. This voltage will need to be extremely stable for accurate result. If offset fluctuates then sample values will fluctuate too. If you can eliminate drift caused by temperature, load fluctuations etc then you can have a very accurate and fast rectifier.

There is a way to make this problem disappear by using a second A/D converter channel. And this is the second drawback if your number of channels is very limited. When using a microcontroller this isnt really a problem as usually there are a sufficient number of channels available.

I usually use the second solution and i always get the VDD/2 value from the second ADC channel and then get the input signal value. Lets look below at at example on how to do this;

/* we will use channel 0 for VDD/2 input and channel 1 for signal input */

int READ_ADC(int channel); //function prototype to read ADC value on channel 

int RECTIFY(void)

{

int INPUT_OFFSET, SAMPLE_VALUE;

INPUT_OFFSET = READ_ADC(0);

SAMPLE_VALUE = READ_ADC(1);

if(SAMPLE_VALUE >= INPUT_OFFSET)

  SAMPLE_VALUE = SAMPLE_VALUE – INPUT_OFFSET;

else

  SAMPLE_VALUE = INPUT_OFFSET – SAMPLE_VALUE;

return SAMPLE_VALUE;

}

 

Output of RECTIFY function can then be stored into a vector and perform various calculations to get your desired result. I usually average the values:

#define SUM_LENGTH 512 //how many samples you want to average

int AVERAGE(void)

{

double SUM=0; // depending on the ADC resolution choose the data type

int RESULT, i;

for(i=0;i<SUM_LENGTH;i++)

{

  RESULT = RECTIFY();

  SUM = SUM + RESULT;

}

RESULT = SUM/SUM_LENGTH;

return RESULT;

}

To choose the SUM_LENGTH you should consider the speed of the A/D conversion and the lowest frequency of the input signal so that you can capture an entire period. This, of course is application dependant but you can play around with the values and check the result.

Holding the RECTIFY value in a vector you can then get max value which is peak value or RMS, envelope and so many more things.

I hope this will be useful and if there is anything you want to ask go ahead and leave a comment.

Thank you for visiting.

Digital Thermometer monitoring up to 10 different areas

facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

Hello, こんにちは,

私の日本語があまり上手くありませんが許して下さい.

One day i was talking with a client and he mentioned that he has a chicken egg incubator and that it is hard to keep track of the temperature inside. I offered my help to build a device that can display the temperature of all the incubators on the same screen.

これは10の分野のためのデジタル温度計です。それがPIC18F4620マイクロコントローラ、LM35温度センサとDigole12864ZW LCDを使用しています。記事の終わりには、Cのソースコード、HEXファイルとPCBレイアウトを見つけることができます。

For this task i used a PIC18F4620 and a Digole 12864ZW graphic LCD. Sensor units (LM35) are separate and i will not discuss it in this article.

The below image (figure 1) shows the electronic schematic of the monitor. J1-10 are 3 pin terminal blocks like the ones in figure 2. Each terminal block provides +5V and ground connection and a pin for sensor data.

Temperature Monitor Digole 12864ZW PIC18F

Figure 1

3 pin TBlock Connector

Figure 2

The circuit is very simple each sensor is connected to one of the ADC inputs of the PIC18F4620 through a 100 ohms resistor and the rest is basically software. The LCD is configured to work in parallel 8 bit mode because it is easier and the microcontroller has enough I/O pins available.

Working with the display was a bit hard in the beginning as the documentation isn’t really clear on some aspects. There weren’t many resources from other DIY-ers that used this display either. I got it to work in graphic mode using a XLCD library from Microchip and Application Maestro to configure it. You can use this library for text mode with no modifications.

I will cover the graphic mode of the Digole LCD in a future article describing in detail how it works.

Once you have generated the XLCD files using application Maestro (setting the data port to port D, RS pin to RB5, RW pin to RB6 and EN pin to RB7, delay mode), you will need to modify XLCD.c file and specifically XLCDinit() function. You can delete it if you want and copy the one below:

void XLCDInit(void)
{
_vXLCDreg=1;

XLCD_DATAPORT_TRIS  = 0x00;
XLCD_DATAPORT = 0;
XLCD_RSPIN_TRIS =0;                        
XLCD_ENPIN_TRIS =0;
XLCD_RWPIN_TRIS =0;
XLCD_RSPIN  =0;                            
XLCD_ENPIN  =0;
XLCD_RWPIN=0;                              
XLCDDelay15ms();
XLCD_DATAPORT   = 0b00110000;  
XLCDDelay4ms();
XLCD_DATAPORT   = 0b00110000;  
XLCDDelay4ms();
XLCDCommand(0x0C);
XLCDDelaylcd();   
XLCDCommand(0x01);
XLCDDelay15ms();  
XLCDCommand(0x06);
XLCDDelaylcd();
XLCDCommand(0x34);
XLCDDelay15ms();    
XLCDCommand(0x36);
XLCDDelay15ms(); 

_vXLCDreg=0;
return;
}

I created functions to display digits and some special characters. Basically these functions accept coordinates as their arguments and draws the specific pixels at the proper address. An example of such a function is below:

void display1(int x, int y)
{
        if(y<27)
    {
            XLCDCommand(0x80 | y);
            XLCDCommand(0x80 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y+1);
            XLCDCommand(0x80 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y+2);
            XLCDCommand(0x80 | x);
                XLCDPut(0b11111000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y+3);
            XLCDCommand(0x80 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);

    }            
    else
    {
            XLCDCommand(0x80 | y-27);
            XLCDCommand(0x88 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y-26);
            XLCDCommand(0x88 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y-25);
            XLCDCommand(0x88 | x);
                XLCDPut(0b11111000);
                XLCDPut(0b00000000);
            XLCDCommand(0x80 | y-24);
            XLCDCommand(0x88 | x);
                XLCDPut(0b00000000);
                XLCDPut(0b00000000);

    }    
}

As can be seen XLCDCommand sets the address and XLCDPut sets the value. These functions are defined in XLCD library from Microchip. The condition set for y coordinate is needed because of the way the LCD is made. It is split in two horizontally. Data width is 16 bits sent in two transmissions of 8 bits each.

Each bit in the 16 bits word represents the state of a pixel so you always draw 8 pixels with each instruction. This can be annoying when trying to write values that change dynamically. So you can’t control one pixel at the time horizontally, but you can control each vertical line. This is why i turned the display on a side and it proved much easier to draw the graphs.

digole 12864ZW PIC18F LCD

digole 12864ZW PIC18F LCD

digole 12864ZW PIC18F LCD

The role of SW1 push button is to switch between the screens on Digole 12864ZW showing time graphs and bar graph for each of the 10 sensors. The program verifies the state of SW1 on each loop so it will require to hold the button for about 1second. Also holding too long might cause the screen to go beyond the area you need.

The button is verified with the switch(counter) function in main(). Case 0 is the first screen displaying all the sensors. Cases 1 -> 10 correspond to individual areas and call the zone(area) function, where area takes a value between 0 and 9.

Here is how the zone(area) function looks like:

void zone(int area)
{
        int i;
        int val_t;
        int val_mem;
        xygraph(); // function that displays the time chart
        bargraph(); // function that displays the bargraph
        // below instructions display 0 Celsius
        XLCDCommand(0x80 | 30);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b11100000);
        XLCDCommand(0x80 | 29);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b10100000);
        XLCDCommand(0x80 | 28);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b11100000);
        XLCDCommand(0x80 | 26);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b01110000);
        XLCDCommand(0x80 | 25);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b10001000);
        XLCDCommand(0x80 | 24);
        XLCDCommand(0x80 | 4);            
        XLCDPut(0b00000000);
        XLCDPut(0b10001000);
        XLCDCommand(0x80 | 26);
        XLCDCommand(0x80 | 6);            
        XLCDPut(0b01110000);
        XLCDPut(0b00000000);
        XLCDCommand(0x80 | 25);
        XLCDCommand(0x80 | 6);            
        XLCDPut(0b10001000);
        XLCDPut(0b00000000);
        XLCDCommand(0x80 | 24);
        XLCDCommand(0x80 | 6);            
        XLCDPut(0b10001000);
        XLCDPut(0b00000000);
        if(SW == 0) // Checking button
        {
            counter++;
        }
        switch(area) //display the area number
        {
            case 0: display0(6,17);break;
            case 1: display1(6,17);break;
            case 2: display2(6,17);break;
            case 3: display3(6,17);break;
            case 4: display4(6,17);break;
            case 5: display5(6,17);break;
            case 6: display6(6,17);break;
            case 7: display7(6,17);break;
            case 8: display8(6,17);break;
            case 9: display9(6,17);break;
        }
        //level display
        val_t=ADC_Read(area); //read the sensor specific to that area
        val_mem=val_t*48; //convert to degrees
        chart(val_mem); // plot value on xychart
        showtemp(val_mem,5,35); // displays the temperature
        showlevel(val_mem); // displays level on bar graph

        for(i=0;i<25;i++) // delay
        {
        XLCDDelay15ms();
        }

}

 

At the end of the article you can download the full C file without the XLCD library also the hex file. Below you can see a version of double sided PCB layout, images are not mirrored:

PCB Temperature Monitor

top silk

PCB Temperature Monitor

Top Copper

PCB Temperature Monitor

Bottom Copper

On the PCB J13 corresponds to SW1 in the schematic.

Temp Monitor PIC18LH4620 Digole 12864ZW

Thank you for visiting,

ご覧いただきありがとうございます。

 

 

Using Digole 12864ZW LCD with PIC18F

PIC18F interface with digole 12864ZW PIC18Fとグラフィック ディスプレイのインターフェイス facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

Hello, こんにちは,

私の日本語があまり上手くありませんが許して下さい.

PIC18Fとグラフィック ディスプレイのインターフェイス

Digole 12864ZW is a 128×64 pixels graphic LCD that can be found at attractive prices and this is why it started appearing in projects across the web. It is based on ST7920 chip which is not so well known and to me it wasn’t the easiest to work with.

私は、グラフィックモードでDigole12864ZWを使用する方法について記述します。C18コンパイラとPIC18F4620を使用しています。

ここで、LCDのデータシートです。

The documentation for this display can be found here. You can find a couple of examples on how to use this display and some attempts on making a library and most of this information is for interfacing with arduino. For PIC interfacing i couldn’t find much info.

The module has a 20 pin connection, each described below. Vout i havent used and left it unconnected. For contrast you can use V0 (pin 3) by connecting it to Vdd through a 10k variable resistor. However to get it to work you will need to enable jumper J1 from the back of the module as in figure 1.

Pin # Function
1 Vss
2 Vdd
3 V0
4 RS
5 RW
6 EN
7 DB0
8 DB1
9 DB2
10 DB3
11 DB4
12 DB5
13 DB6
14 DB7
15 PSB
16 NC
17 RST
18 VOUT
19 BLA
20 BLK
Digole 12864ZW PIC18F contrast

Figure 1

As per the datasheet the display can be used in many ways and this is a strong feature. I used it the traditional way 8 bits parallel. For this, PSB pin must be connected to Vdd, i also connected RST pin to Vdd as i will reset the display in software. The rest of the connections are the same as with any 16×2 alphanumeric displays.

コントラストが機能するためには、J1を有効にする必要があります。写真1を参照してください。

Microchip XLCD library is a great starting point to using the Digole 12864ZW with a PIC microcontroller. I used Application Maestro to set up the library. As mentioned we will use 8 bits interface in delay mode. Set the ports and pins and the rest doesn’t really matter.

アプリケーションマエストロとのXLCDファイルの設定。

Application Maestro XLCD

Figure 2

Once done pressing Ctrl+G will generate the library files to be included in your project. You will need the XLCD.h, XLCD.c and XLCD.def. Now this Digole display can run in text mode with this library with major issues.To get to each line though you will need to go to specific addresses.

To use the LCD in graphic mode we will need to change the XLCDinit function in XLCD.c file. You can comment the one you have now and then copy paste the function below:

以下のコードでXLCD.cファイルにXLCDInitコードを交換してください。

void XLCDInit(void)
{
_vXLCDreg=1;

XLCD_DATAPORT_TRIS  = 0x00; 
XLCD_DATAPORT = 0;
XLCD_RSPIN_TRIS =0;                         
XLCD_ENPIN_TRIS =0;
XLCD_RWPIN_TRIS =0;
XLCD_RSPIN  =0;                             
XLCD_ENPIN  =0;
XLCD_RWPIN=0;                               
XLCDDelay15ms(); 
XLCD_DATAPORT   = 0b00110000;   
XLCDDelay4ms();
XLCD_DATAPORT   = 0b00110000;   
XLCDDelay4ms(); 
XLCDCommand(0x0C); 
XLCDDelaylcd();   
XLCDCommand(0x01); 
XLCDDelay15ms();  
XLCDCommand(0x06); 
XLCDDelaylcd();
XLCDCommand(0x34); 
XLCDDelay15ms();    
XLCDCommand(0x36); 
XLCDDelay15ms(); 

_vXLCDreg=0;
return;
}

The other two functions we will use from the XLCD library are XLCDCommand and XLCDPut, which are used to pass a command to the display and a value respectively. Using these functions you don’t have to worry about controlling RS , RW and EN.

After calling the XLCDinit() function in main() you have now setup the display in graphic mode. To start drawing you need first 2 XLCDCommand instructions that set the pixel address and then 2 XLCDPut instructions to draw the pixels.

In figure 3 you can see the address map of Digole 12864ZW in the operating mode described above. It can be observed the horizontally you cannot address pixels individually but in groups of 16 and values you can write in groups of 8. The 1st byte represents the byte in the first XLCDPut instruction and the 2nd byte the value in the 2nd instruction. You cannot make 1 byte to start at the middle of the address location. We have 8 addresses of 16 pixels each horizontally 8×16=128 pixels and 64 lines =>128×64 resolution.

Digole12864ZWグラフィックアドレスマップ

Digole 12864 Address Map アドレスマップ

Figure 3

//Example:

/* In figure 3 there are 4 red pixels. To set the pixel we need the following instructions:

Note that we are in region where x=0 => */

XLCDCommand(0b10010010); // 1st instruction

XLCDCommand(0b10000001); // 2nd instruction

XLCDPut(0b00110001); // 1st byte, red pixels are 1 in value

XLCDPut(0b00000010); // 2nd byte, red pixel is 1 in value

/* As you can see the position of the pixels in the two bytes are the positions of 1 in the XLCDPut values */

So to draw a horizontal line of a specific length you will need to take in consideration the fact that you will always set all 16 bits in one of the 8 horizontal addresses.

//Example: Draw line from pixel 41 to end of screen at line 10

/* x = 41/16 = 2; x_r = 41%16 = 9; This means we will start the line from the 2nd byte of horizontal address 010. y = 10 => H_ADDRESS = 0b10000010 */

// lets draw the first part of line 

int index_x, index_y, i;

index_y = 10;

index_x = 41/16;

XLCDCommand(0x80 | index_y); // V_ADDRESS

XLCDCommand(0x80 | index_x); //H_ADDRESS

XLCDPut(0x00); //1st byte is zero

XLCDPut(0x7F); //x_r%8 = 1 => values is 0xFF >> 1 meaning 0b01111111

//now draw to end of screen

for(i=3;i<8;i++)

{

XLCDCommand(0x80 | index_y);

XLCDCommand(0x80 | i);

XLCDPut(0xFF);

XLCDPut(0xFF);

}

Another good reason to use graphic mode is to create your own fonts for text. In its own text mode the display’s characters are very large and can only fit 4 lines. I have created my own set of characters of 7×7 pixels. I have used the fact that each horizontal address has two bytes so that i can fit a character’s width in one byte and leave 1 empty pixel as space. In Figure 4 you can see the W character and its pixel representation. It uses 1 byte on 7 lines so 7 bytes.

 

Character map DIgole 12864 display

Figure 4

This way each character can be represented as a vector of 7 bytes:

int chW[7]={ 146, 146, 146, 146, 146, 170, 68 };

To display a string i separate each character and based on its value i fill a matrix of [7][8] dimension representing a text line on the display. To use it easily the matrix is declared as follows:

union writebyte
{
struct
{
char LowByte;
char HighByte;
};

}linebytes[7][8];

This way you can easily call XLCDPut functions with LowByte or HighByte as arguments. Here is the case for W within the character switch function:

case ‘w':
{
for(j=0;j<7;j++)
{
if(k%2 == 0)
{
linebytes[j][k/2].LowByte=chW[j];
}
else
{
linebytes[j][k/2].HighByte=chW[j];
}
}
}break;

where j is the line number and k is the character number. Below you can see how it looks like:

 PIC18Fとグラフィック ディスプレイのインターフェイス

 PIC18Fとグラフィック ディスプレイのインターフェイス interfacing PIC18F with Digole 12864ZW

 PIC18Fとグラフィック ディスプレイのインターフェイス interfacing PIC18F with Digole 12864ZW

 

In the zip archive below you can find the font.h containing definitions for characters seen above and an example code for PIC18F4620 that displays the message stored in msg[] variable.

I will add more characters and functions with time but i hope this was helpful for anyone trying to interface Digole 12864ZW LCD to a PIC18F.

フォントの収集とPIC18F4620用のサンプル·コード

Digole 12864W sample code for PIC18F4620

Thank you for visiting,

ご覧いただきありがとうございます。

Using Software SPI with TFT Touchscreen

facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

Hello,

In the previous article i wrote how you could implement a software SPI communication. These days i tried this solution with a TFT touchscreen using TSC2046 driver. This will also work for ADS7843 driver.

Basically you will need to call the Get_Value_SSPI function described in the previous article. As argument if we look in datasheet of TSC2046 or ADS7843 we see we need 0xD0 for x values and 0x90 for y values.

Example:

UINT16 TS_X, TS_y;

int x,y;

TS_X = Get_Value_SSPI(0xD0);

TS_Y = Get_Value_SSPI(0x90);

/* TS_X, TS_Y hold the ADC values from the touchscreen driver we need to convert them to coordinates. I cut last two digits as i dont need high precision and then scaled the values to correspond to a 800×480 TFT */

x=(TS_X/100)*1.26;

y=(TS_Y/100)*0.76;

I recorded a video of how it works, hope you enjoy it:

Thank you for visiting.

 

Implementing Software SPI for PIC 2 PIC Communication

facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

Hello,

In one of my projects i had to transmit some data between two PIC microcontrollers. The data represented some sensor values and it was important to know which value corresponded to which sensor.

This can be tricky if you don’t have free peripherals so i will present a way to implement SPI style communication by bit-banging any pin/port available. As with most SPI you will need 4 wires representing Clock, MISO (Master In Slave Out), MOSI (Master Out Slave In) and CS (Chip Select or Slave Select or Data Enable….).

Scenario is like this PIC1 MCU converts analog value of a few sensors with a 16bit resolution and then sends the data to PIC2 MCU for further processing. PIC2 needs to know that the data that was just received corresponds to a certain sensor. When can achieve this simply by letting PIC2 make a data request to PIC1.

E.g.

PIC2: [SEND DATA OF SENSOR 3]—————->:PIC1

PIC1:[SENDING SENOR 3 DATA]——————>:PIC2

PIC2: [SEND DATA OF SENSOR 7]—————->:PIC1

PIC1:[SENDING SENOR 7 DATA]——————>:PIC2

This way PIC2 will always associate the received data with the right sensor. It seems natural that PIC2 will be the SPI Master and PIC1 will be the slave. The Master is the one initiating the communication by first setting CS line to logic low and then starting the clock. It will then send the sensor address to the slave and receive the value from the slave. Once value was read it will stop the clock and set CS to logic high.

I assigned each sensor an address, 8 bit is enough allowing basically for 256 sensors. The received data is 16 bits long.

1. SPI MASTER program

/* I will use delay functions which you will need to define according to your compiler

and your Fcy settings. */

//Defining Signal Lines, replace “x” chars with your port/pin numbers

#define DCLK PORTxbits.Rxx
#define DIN PORTxbits.Rxx //MISO
#define DOUT PORTxbits.Rxx //MOSI
#define CS PORTxbits.Rxx

//Function to configure the data direction

//If you add these functions after main() remember to add prototype before main()

void Init_MSPI(void)

{

TRISxbits.TRISxx = 0; // clock
TRISxbits.TRISxx = 1; // DIN
TRISxbits.TRISxx = 0; // DOUT
TRISxbits.TRISxx = 0; // CS

//look in your MCU h file to get the actual TRIS struct parameters

CS=1;
DCLK=1;

}

UINT16 Get_Value_SSPI(char ADDRESS)

{

char temp; //used to serialize data

UINT16 in=0x0000; //will hold received 16bit data, initialize with 0

CS=0;

__delay_us(50);

//CS Line set to logic low

//begin serializing ADDRESS and sending it via DOUT from MSB to LSB

for(i=0;i<8;i++)
{
temp=ADDRESS&0x80; //Keep the MSB, ignore the rest of the bits
if(temp) // if MSB is 1 put 1 on DOUT and cycle clock 
{
DOUT = 1;
DCLK = 0;
__delay_us(50);
DCLK = 1;
__delay_us(50);
}
else //if MSB is 0 put 0 on DOUT and cycle clock
{
DOUT = 0;
DCLK = 0;
__delay_us(50);
DCLK = 1;
__delay_us(50);
}
ADDRESS <<=1; // Shift data to the right to get the next bit in MSB position
}

//cycle clock again, prepare to receive
DCLK = 0;
__delay_us(50);
DCLK = 1;
__delay_us(50);

//receiving the data
for(i=0;i<16;i++)
{
DCLK = 0; //activate clock
if(DIN) // if on DIN line we have 1 we put 1 in LSB position of “in” variable
in|=0x0001;
__delay_us(50);
DCLK = 1;
__delay_us(50);

//if DIN 0 leave variable unchanged, cycle clock
in<<=1; // shift LSB to right and prepare to receive the next bit
}

 //reception of last bit needs to be out of for loop so it wont get shifted to the right

if(DIN)
in|=0x0001;
DCLK = 0;
__delay_us(50);
DCLK = 1;
__delay_us(50);

//cycle clock and close communication by putting CS line to logic high

CS=1;
__delay_us(50);

}

// to call the function in main

UINT16 Sensor_Value;

Sensor_Value = Get_Value_SSPI(ADDRESS);

2. SPI Slave Program

//Defining Signal Lines, replace “x” chars with your port/pin numbers

#define DCLK PORTxbits.Rxx
#define DIN PORTxbits.Rxx //MISO
#define DOUT PORTxbits.Rxx //MOSI
#define CS PORTxbits.Rxx

//Function to configure the data direction

//If you add these functions after main() remember to add prototype before main()

void Init_SSPI(void)

{

TRISxbits.TRISxx = 1; // clock
TRISxbits.TRISxx = 1; // DIN
TRISxbits.TRISxx = 0; // DOUT
TRISxbits.TRISxx = 1; // CS

//notice clock and CS are inputs on slave MCU

}

void Send_Value_MSPI(void)

{

char address=0x00; //variable to hold sensor address

UINT16 out,temp; //out holds the sensor value, temp used to serialize data

while(!CS);
while(CS);

//wait for CS line to cycle to logic low

//this prevents the Slave to start its routine in the middle of communication

//start receiving Master data on DIN
for(i=0;i<8;i++)
{
if(DIN) //if DIN is 1 put 1 in data as LSB if 0 leave data unchanged
data|=0x01;
data<<=1; //shift LSB to the right, wait for clock and prepare for new bit
while(!DCLK);
while(DCLK);
}
if(DIN)//last bit must be out of for loop to prevent shifting it to the right
data|=0x01;

/*wait 2 clock periods in here you should get the value corresponding to the received address */
while(!DCLK);
while(DCLK);
while(!DCLK);
while(DCLK);

// serialize the value and send it bit by bit on DOUT

for(j=0;j<16;j++)
{
temp = out&0x8000;// keep MSB and ignore the res
if(temp) //toggle DOUT depending on MSB
DOUT=1;
else
DOUT=0;
out<<=1;//shift data to the right prepare next bit as MSB and wait for clock
while(!DCLK);
while(DCLK);
}
DOUT=0; // once all bits are sent put DOUT to 0 and wait final clock
while(DCLK);
while(!DCLK);

}

Ran the software on 2 PIC24FJ128GA010 devices and below you can see the signals. Remember DOUT form Master goes to DIN on Slave and DIN from Master Goes to DOUT on Slave. From top to bottom 1st signal = Clock, 2nd signal = Master DOUT, 3rd signal = Master DIN, 4th signal = CS

ADDRESS = 0xC2 Sensor_Value = 0xF10F

SPI Communication PIC

 

 

to see if each PIC MCU will interpret the data correctly i added a verification point on both programs by making a pin go high if the data is verified. Below you can see the signals.

SPI Communication PIC

showing control output and data lines

SPI Communication PIC

Showing control output with Clock and CS lines

 

You can change delay values in the Master program for faster or slower transmission, just make sure you have the same value everywhere. The beauty part is that you can use it with any ports/pins and not worrying about your peripherals.

Thank you for visiting.

Temperature Effect Analysis Tool

facebooktwittergoogle_plusredditpinterestlinkedinmailby feather

 

こんにちは,

私は、スピーカーに対する温度の影響を監視するために開発したツールをご紹介したいと思います。これは、Microchip社のPIC18F4550マイクロコントローラーを中心に構築されており、
USBポート経由でPCに接続されています。デスクトップアプリケーションは、私のパートナーがC#でプログラムをしました。

 

Hello everyone,

I want to present a tool I developed for monitoring temperature effects on loudspeaker performance. It is built around PIC18F4550 microcontroller from Microchip and it connects to a PC via USB port. Desktop application was programmed in C# by one of my partners.

DSC03402

The tool uses a reference resistor to calculate the changes in field coil and voice coil resistance. The voltage captured is fed into the 10 bit ADC on the microcontroller. It is also equipped with a LM35 temperature sensor in order to calculate the voice coil’s temperature coefficient. This parameter can be used to predict power compression.

DSC03388 DSC03389 DSC03390

 

The Desktop application will display graphs showing the time variation of four parameters. Data can also be saved for further processing.

tea