PIC: I can't send data using SPI. I am using PIC10f222. - Page 1
Forum section: electronics / general

author: armaganergin
author_rank: Newbie
timestamp: March 14, 2025, 09:23:27 am
content: Hi, I need to send data to ADF4002 frequency synthesizer using 8 bit microcontroller PIC10f222. I have never programmed embedded systems and have not worked on PIC microcontrollers. They asked me to do this for a project at my internship.PIC10f222 don't have any SPI libraries. Chatgpt said you should do bit banging. I wrote the code myself, but the clock was not periodic, probably due to the arithmetic operations in the for loop.I need stable clock pulse and I also need to send data simultaneously.Code:void SPI_send(uint8_t data){for (int i = 7; i >= 0; i--) { // send bitsGP0 = (data >> i) & 1;// wait for clockwhile (SCLK_PIN == 0){__nop();}}Thanks for helping.

author: bookaboo
author_rank: Frequent Contributor
timestamp: March 14, 2025, 09:46:06 am
content: Could you post all your code?is your device a SPI master or slave?Have you set the relevant TRIS registers on the GP0and SCLK_PIN ?How are SCLK_PIN and GP0 defined?

author: squadchannel
author_rank: Frequent Contributor
timestamp: March 14, 2025, 09:53:40 am
content: A few points of concern.1) no parentheses at the end of for loop.2) AND'ing out 1bit in GP0, but it would look better if you used "& 0b1" instead of "& 1". 1 is a decimal number; 0b makes it a bit, i.e., a binary. There will be fewer misunderstandings.3) NOP is not needed. "while(SCLK_PIN == 0);" is fine.Clock generation code is required. Full code would be better.if you have scope, an pics of the output bitstream would be nice.

author: armaganergin
author_rank: Newbie
timestamp: March 14, 2025, 10:08:19 am
content: spi.cCode:#include "spi.h"#include <xc.h>#include <stdbool.h>#define _XTAL_FREQ 4000000#define SCLK_PIN GP2#define MOSI_PIN GP0#define MISO_PIN GP3#define SS_PIN GP1 //active lowvoid SPI_init(){ADCON0 = 0;OPTION = 0b00000111;TRISGPIO = 0b11111000; // GP3 is input the others are outputOSCCAL = 0b00000001; // setting GP2 as a clock output from internal clockSCLK_PIN = 0;MOSI_PIN = 0;SS_PIN = 1;}void SPI_send(uint8_t data){for (int i = 7; i >= 0; i--){MOSI_PIN = (data >> i) & 1;while (SCLK_PIN == 0);}}main.cCode:#include "spi.h"#define DUMMY_DATA 0b10101010#pragma config MCLRE = OFF // MCLR pini devre d??? (GPI olarak kullan?labilir)#pragma config CP = OFF // Kod korumas? kapal?#pragma config WDTE = OFF // Watchdog Timer kapal?#pragma config MCPU = OFF // Dahili pull-up etkin (MCLR pini için)#pragma config IOSCFS = 4MHZ // Dahili osilatör 4MHz olarak ayarland?void main(){SPI_init();while(1){SPI_send(DUMMY_DATA);}}NotesI have tried various things.Code:while(1){GP2 = 1;SPI_send(DUMMY_DATA);GP2 = 0;__delay_us(20);}For example this code is not giving me proper clock signal. I tried this with different ways too. Should I write it in assembly?My device is master and we don't need to read data. We need to send them.

author: squadchannel
author_rank: Frequent Contributor
timestamp: March 14, 2025, 10:23:12 am
content: Quote from: armaganergin on March 14, 2025, 10:08:19 amThis code does not work of course.The clock pulse need with each byte. In this code, it pulses after 8 bytes are sent.replace SPI_send code withCode:void SPI_send(uint8_t data){for (int i = 7; i >= 0; i--){MOSI_PIN = (data >> i) & 1;GP2 = 1;//clock//_nop();//clock pulse widthGP2 = 0;}}

author: kripton2035
author_rank: Super Contributor
timestamp: March 14, 2025, 10:31:10 am
content: you can also ask chatgpt to write the code for you. it is quite good for small embedded devices code like this.I did not test it, I usually have the device I test on a nearby breadboard, but it is generally a good start.QuoteCode:#include <xc.h>// PIC10F222 Configuration bits (example)#pragma config WDTE = OFF, CP = OFF, MCLRE = OFF// Define GPIO pins for SPI#define MOSI GP0#define CLK GP1// Simple delay functionvoid delay_us(unsigned int us) {while(us--) {_nop(); // Approximately 1 µs delay (adjust depending on oscillator)}}// Initialize GPIOvoid init_gpio(void) {TRIS = 0b0000; // All pins as outputsGPIO = 0x00; // Clear GPIO pins}// Bit-banging SPI function to send one bytevoid spi_send_byte(unsigned char data) {for (int i = 7; i >= 0; i--) {MOSI = (data >> i) & 0x01; // Set MOSI pin with current bitdelay_us(1);CLK = 1; // Rising edge of clockdelay_us(1);CLK = 0; // Falling edge of clockdelay_us(1);}}void main(void) {init_gpio();while(1) {spi_send_byte(0xA5); // Example: sending byte 0xA5 repeatedlydelay_us(100);}}

author: armaganergin
author_rank: Newbie
timestamp: March 14, 2025, 10:39:59 am
content: Quote from: squadchannel on March 14, 2025, 10:23:12 amThanks for the help but I have tried it too. Clock pulses looks like impulses it must be square wave.

author: squadchannel
author_rank: Frequent Contributor
timestamp: March 14, 2025, 10:59:17 am
content: Pulse means a momentary waveform such as a square wave or like, and the picture of the scope shows a 2bits transfering.Use the unused SS pin as a trigger signal for the scope.ConnectCh1 to SCLKCh2 to MOSICh3 to the SS pinand rewrite toCode:void SPI_send(uint8_t data){SS_PIN = 0;//enabling slave devicefor (int i = 7; i >= 0; i--){MOSI_PIN = (data >> i) & 1;GP2 = 1;//clock//_nop();//clock pulse widthGP2 = 0;}SS_PIN = 1;}Then set the scope to trigger negative edge, ch3 and set the time width of the scope so that you see 8 pulses(negative pulse width of the ch3 SS pin).and check that it is being output. It should be output.

author: xvr
author_rank: Frequent Contributor
timestamp: March 14, 2025, 12:11:56 pm
content: QuoteAdd appropriate delay betweenGP2=1andGP2=0and it will be.

author: xvr
author_rank: Frequent Contributor
timestamp: March 14, 2025, 12:15:17 pm
content: You can also slightly optimize code, to minimize loop contents:Code:void SPI_send(uint8_t data){SS_PIN = 0;//enabling slave deviceuint8_t i = 8;do{MOSI_PIN = data & 1;GP2 = 1;//clock_nop();//clock pulse widthdata >>= 1;GP2 = 0;}while(--i);_nop();SS_PIN = 1;}

author: squadchannel
author_rank: Frequent Contributor
timestamp: March 14, 2025, 12:38:53 pm
content: depending on the allowable clock pulse width of the ADF4002, NOP is also not required.

author: armaganergin
author_rank: Newbie
timestamp: March 14, 2025, 12:56:51 pm
content: Quote from: xvr on March 14, 2025, 12:15:17 pmYour code works pretty good. Thank you very much. I will optimize the code and post here next week.

author: mikerj
author_rank: Super Contributor
timestamp: March 14, 2025, 01:01:09 pm
content: Quote from: armaganergin on March 14, 2025, 10:39:59 amWhy must it? Provided you meet the setup/hold timing requirements of the slave device it's not necessary to have equal high and low periods.

author: Ian.M
author_rank: Super Contributor
timestamp: March 14, 2025, 01:39:45 pm
content: Bit-banging individual pins on a Microchip Baseline (or 'classic' Midrange) PIC is problematic, and depending on pin loading and timing + other external factors, may not give the results you expect. See'RMW and solutions for it' @Microchip Forums. Often you can get away with it, but 'here be dragons'!Its probably best to use the XC8 builtin delay functions - in this case probably __delay_us() rather than coding your own delay routine as the compiler will adjust the generated code to get as close as possible to the specified delay, and if it generates a subroutine will auto-compensate for call overhead. Seehttps://microchip.my.site.com/s/article/Delays-in-an-8-bit-codeOn optimisation, try:Code:void SPI_send(uint8_t data){for (uint8_t mask = 0x80; mask; mask>>=1){MOSI_PIN = !!(data & mask);SCLK_PIN = 1;//clock//_nop();//clock pulse widthSCLK_PIN = 0;}}which uses the bit mask as the loop variable. You'll need to look at the assembler listing to check if it is better than armaganergin's suggestion.XC8 at low optimisation levels has historically been poor at assigning non-constant expressions to bit variables, so it may be worth tryingCode:MOSI_PIN=0;if(data & mask) {MOSI_PIN=1;}replacing the MOSI_PIN line above.The SS_PIN should not be manipulated in SPI_send(), as it must be set low before the first byte of the transfer and remain low for all the bytes, then be set high after the last byte.