GIỚI THIỆU VỀ SPI MỀM
Bài viết hôm nay tôi sẽ giới thiệu
tới
các bạn
một
chuẩn
truyền
thông khá phổ
biến
và thông dụng trong lĩnh vực lập trình nhúng. Nó được ứng dụng rộng rãi trên hầu hết các dòng chip. Đó là chuẩn truyền
thông SPI. Bài viết được tôi chia làm 3 phần chính. Đầu tiên tôi sẽ giới thiệu về
giao thức SPI. Tiếp theo tôi sẽ giới thiệu một các tiếp cận mới trên chuẩn SPI
là “SPI mềm” và cuối cùng tôi sẽ đưa ra một ví dụ để các bạn tiện theo dõi.
I.
Vậy SPI là gì?
1.
Trước
hết
chúng ta cùng tìm hiểu
SPI là gì?
SPI
(Serial Peripheral Bus) là
một chuẩn truyền thông nối tiếp tốc độ cao do hãng Motorola đề xuất. Đây là
kiểu truyền thông Master-Slave, trong đó có 1 chip Master điều phối quá trình
tuyền thông và các chip Slaves được điều khiển bởi Master vì thế truyền thông
chỉ xảy ra giữa Master và Slave. SPI là một cách truyền song công (full duplex)
nghĩa là tại cùng một thời điểm quá trình truyền và nhận có thể xảy ra đồng
thời. SPI đôi khi được gọi là chuẩn truyền thông “4 dây” vì có 4 đường giao
tiếp trong chuẩn này đó là SCK (Serial Clock), MISO (Master Input Slave
Output), MOSI (Master Ouput Slave Input) và SS (Slave Select). Hình 1 thể hiện một kết SPI giữa một
chip Master và 3 chip Slave thông qua 4 đường.
SCK: Xung giữ nhịp cho giao tiếp SPI, vì SPI là chuẩn truyền đồng bộ nên cần
1 đường giữ nhịp, mỗi nhịp trên chân SCK báo 1 bit dữ liệu đến hoặc đi. Đây là
điểm khác biệt với truyền thông không đồng bộ mà chúng ta đã biết trong chuẩn
UART. Sự tồn tại của chân SCK giúp quá trình truyền ít bị lỗi và vì thế tốc độ
truyền của SPI có thể đạt rất cao. Xung nhịp chỉ được tạo ra bởi chip Master.
MISO – Master
Input / Slave Output: nếu là chip Master thì đây là đường
Input còn nếu là chip Slave thì MISO lại là Output. MISO của Master và các
Slaves được nối trực tiếp với nhau.
MOSI – Master Output / Slave Input: nếu là chip Master thì đây là đường Output còn nếu là chip Slave thì MOSI là Input. MOSI của Master và các Slaves được nối trực tiếp với nhau.
MOSI – Master Output / Slave Input: nếu là chip Master thì đây là đường Output còn nếu là chip Slave thì MOSI là Input. MOSI của Master và các Slaves được nối trực tiếp với nhau.
SS – Slave
Select: SS là đường chọn Slave cần giap tiếp, trên các chip
Slave đường SS sẽ ở mức cao khi không làm việc. Nếu chip Master kéo đường SS
của một Slave nào đó xuống mức thấp thì việc giao tiếp sẽ xảy ra giữa Master và
Slave đó. Chỉ có 1 đường SS trên mỗi Slave nhưng có thể có nhiều đường điều
khiển SS trên Master, tùy thuộc vào thiết kế của người dùng.
Hình 1. Chuẩn truyền thông SPI |
Đôi
khi chuẩn SPI được sử dụng chỉ để ghi dữ liệu từ Master ra Slaver thì chân MISO
sẽ không được dùng.
2. Cơ chế hoạt
động: mỗi
chip Master hay Slave có một thanh ghi dữ liệu 8 bits. Cứ mỗi xung nhịp do
Master tạo ra trên đường giữ nhịp SCK, một bit trong thanh ghi dữ liệu của
Master được truyền qua Slave trên đường MOSI, đồng thời một bit trong thanh ghi
dữ liệu của chip Slave cũng được truyền qua Master trên đường MISO. Do 2 gói dữ
liệu trên 2 chip được gởi qua lại đồng thời nên quá trình truyền dữ liệu này được
gọi là “song công”. Hình 2 mô tả quá
trình truyền 1 gói dữ liệu thực hiện bởi module SPI trong AVR, bên trái là chip
Master và bên phải là Slave.
Hình 2. Truyền dữ liệu SPI |
3. Các chế độ hoạt động
- Cực của xung
giữ nhịp (Clock Polarity): được gọi tắt là CPOL là khái niệm dùng chỉ
trạng thái của chân SCK ở trạng thái nghỉ. Ở trạng thái nghỉ (Idle), chân SCK có thể được giữ ở mức
cao (CPOL=1) hoặc thấp (CPOL=0).
- Phase (CPHA):
dùng để chỉ cách mà dữ liệu được lấy mẫu (sample)
theo xung giữ nhịp. Dữ liệu có thể được lấy mẫu ở cạnh lên của SCK (CPHA=0) hoặc cạnh xuống (CPHA=1).
Sự kết hợp của SPOL và CPHA làm nên 4 chế độ hoạt động
của SPI. Nhìn chung việc chọn 1 trong 4 chế độ này không ảnh hưởng đến chất
lượng truyền thông mà chỉ cốt sao cho có sự tương thích giữa Master và Slave.
Khi giao tiếp vi điều khiển giữa các ic khác các bạn phải chú ý xem ic đó hoạt
động ở chế độ nào mà cấu hình vi điều khiển cho phù hợp
2. Khái niệm về “SPI mềm”?
Trên mỗi dòng vi điều khiển khác nhau module
SPI sẽ được tích hợp, điều khiển bởi các thanh ghi,phần cứng, IO khác nhau, đấy
gọi là SPI cứng (hardware SPI). Như vậy
bản chất chuẩn truyền thông SPI giống nhau trên mỗi chip nhưng lại được cài đặt
và sử dụng không giống nhau. Điều này gây thêm phiền toái cho người sử dụng khi
bạn bắt đầu tìm hiểu một dòng vi điều khiển mới, bạn sẽ phải nhớ các chân MISO,
SS, MOSI, SCK mỗi chip khác nhau, nhớ các thanh ghi, các chế độ hoạt động và
cách cài đặt trên các dòng vi điều khiển khác nhau. Có cách nào khắc phục điều
này không?
Để khắc phục nhược điểm
trên tôi xin giới thiệu một cách lập trình giả lập SPI cứng đó là “SPI mềm”. Thực
chất SPI mềm là cách “bắt trước” bằng cách tạo ra một giao thức truyền thông giống
SPI nhưng chỉ sử dụng các cổng vào ra của vi điều khiển. Như vậy chỉ với việc
điều khiển GPIO của chip bạn hoàn toàn có thể thực hiện giao thức SPI. Điều này
cũng có nghĩa bạn có thể sử dụng giao thức này trên bất kì vi điều khiển nào mà
không cần phải nhớ thanh ghi hay các chân phần cứng.
3. Ví dụ về cách sử dụng SPI mềm
Sau đây tôi xin trình
bày một cách chi tiết việc sử dụng SPI mềm trên một ví dụ để các bạn cùng tham
khảo:
Hình 4. Sơ đồ nguyên lý ghép nối ATMEGA 16 và MCP41010 |
Trên hình chip ATMEGA
16 được sử dụng như một master, còn MCP41010 là slaver. Chip MCP41010 thực chất
là một DAC 8 bits giao tiếp bằng chuẩn SPI. Đầu vào là 1 số 8 bits và đầu ra là
mức điện áp tương ứng trên chân PW0 (chân số 6).
·
Chân số 1: CS (chip select) tương ứng
chân SS(trong chuẩn SPI) đã được giới thiệu ở trên, chip thực hiện việc giao tiếp
với Master khi chân này ở mức logic 0.
·
Chân số 2 là chân SCK giữ nhịp cho đường
truyền.
·
Chân số 3 SI (slaver input) là chân dữ
liệu truyền từ Master sang Slaver(Giống chân MOSI), dữ liệu từ MCP41010 không
phản hồi ngược lại vi điều khiển nên không có chân MISO.
·
Chân PA0 (chân 5) được sử dụng làm điện
áp tham chiếu đầu ra. Chip MCP41010 thực
chất là bộ chuyển đổi DAC 8 bits, đầu vào là giá trị số có giá trị từ 0-255(tương
ứng 8 bits) gọi là Vin, và đầu ra cho mức điện áp tương ứng 0V-Vref(điện áp
tham khảo trên chân PA0). Điện áp ta thu được trên chân PW0 được tính theo công
thức:
Vra=Vin/255*Vref;
Vref ở đây là giá trị điện áp tham chiếu được sử dụng trên chân PA0 (có giá trị
Vref=2.7-5.5V);
·
Chi tiết chip MCP41010 tham khảo datasheet
tại: http://www.allMOSIsheet.com/MOSIsheet-pdf/pdf/91948/MICROCHIP/MCP41010.html
Bước đầu tiên ta định
nghĩa cho 3 chân sử dụng trong chương trình:
#define SCK PORTC.0 // chân SCK
#define MOSI PORTC.1 // Chân MOSI
#define SS PORTC.2
// Chân SS
#define MISO PORTC.3 // chân MISO
trong bài không dùng tới
Khai báo các chương
trình con được sử dụng trong chương trình
void SPI_init();// chương trình khởi
tạo SPI
void clock();// chương trình tạo 1
xung clock
void send_byte(unsigned char MOSI);//
chương trình gửi 1 byte từ ATMEGA 16 ra chip
unsigned char receiver_byte();//
chương trình nhận về 1 byte MCP41010.
Sau đây chúng ta đi
tìm hiểu kĩ các hàm có trong chương trình.
Đầu tiên là hàm khởi tạo SPI:
void SPI_init()
{
DDRC=0xf7; // Cấu hình chân
MOSI,SS, SCK là cổng ra và MISO là cổng vào
SCK=0; // Đặt MCP41010 ở trạng thái chưa hoạt động
MOSI=0;
SS=1;
}
Hàm tạo xung clock.
void clock()
{
SCK=1;delay_us(5);
SCK=0;delay_us(5);
}
1 xung clock được phát ra thì 1 bits sẽ được gửi đi hoặc đọc
về trên đường dữ liệu,tốc độ gửi hay đọc phụ thuộc vào thời gian phát 1 xung
clock,các bạn sẽ tùy vào từng thông số từng chip khác nhau để thời gian
delay_us(x) tương ứng.
Hàm gửi 1 byte từ
ATMEGA 16 ra MCP41010
void send_byte(unsigned char data)
{
unsigned int i=0,x=0;// byte cao có giá trị
0x11 là mã lệnh ghi vào chip của MCP41010
SS=0;// cho phép chip MCP 41010 hoạt động
for(i=0;i<8;i++)// gửi ra 1 byte tương ứng
8 bits
{
x=data&0x80;// đưa bit cần truyền lên chân MOSI
if (x>0) MOSI=1;
else MOSI=0;
clock();// phát 1 xung clock gửi dữ liệu đi
data=data<<1;// dịch dữ liệu cần gửi lên 1 bits
}
SS=1;//Đã gửi xong,đưa chân SS lên mức 1
}
Các bạn thấy đó thật sự
giao tiếp qua SPI mềm chương trình cực kì đơn giản dễ thực hiện. Với chip MCP
41010 thì không có chiều dữ liệu từ Slaver tới Master tuy nhiên với những chip
khác có chiều dữ liệu từ Slaver tới Matster thì hàm đọc dữ liệu vô cùng đơn giản:
unsigned char receiver_byte()//
chương trình nhận về 1 byte
{
unsigned char data=0x00,i=0;
while(i<8)
{
data=data<<1;// dịch dữ liệu lên 1 bit
data=data|MISO;//đọc chân MISO
clock();// phát một xung clock để đọc bit tiếp theo
}
return data;// trả về dữ liệu đọc được
}
Mình sẽ thực hiện ví dụ
tạo ra 1 điện áp nửa sin có biên độ 5V trên codevision
Trước khi thực hiện ví
dụ các bạn khai báo thêm một số thư viện:
#include <mega16.h>
#include <math.h>
#include <delay.h>
unsigned int i=0;
unsigned char MOSI;
float h=(3.1415/50);// tạo ra một
hình sin 50 bậc trên nửa chu kì
Cuối cùng ví dụ trong
thân hàm main:
void main(void)
{
SPI_init();// Khởi tạo SPI
send_byte(0x00);// Xóa sạch đầu ra
delay_ms(20);
unsigned char data=0;
while (1)
{
for(i=0;i<50;i++)
{
data=(unsigned
char)((5*sin(h*i))/5.0*255);// gửi ra giá trị điện áp cần tạo ra
send_byte(0x11);// đây là mã lệnh yêu
cầu ghi giữ liệu vào MCP41010
send_byte(data);// dữ liệu cần chuyển
đổi
delay_ms(30);
}
}
}
Xin cám ơn các bạn đã theo dõi blog.
2 nhận xét:
liên hệ với tác giả qua email nào nhỉ :)
Xin bạn 1 bài mẫu về điều khiển 2 SPI, ví dụ như thực hiện copy từ SD sang SD khác, thanks!
Đăng nhận xét