Professional Documents
Culture Documents
BTN4 V5
BTN4 V5
BTN4 V5
BTN #4: BÀI THÍ NGHIỆM LẬP TRÌNH CARD USB GIAO
TIẾP VỚI MÁY TÍNH
A. MỤC ĐÍCH THÍ NGHIỆM
● Biết cách cấu hình ngoại vị cho vi điều khiển STM32 sử dụng phần mềm
STM32CubeMX và phần mềm Keil-C.
● Biết cách lập trình một giao diện chương trình C# bằng phần mềm Visual Studio.
● Tìm hiểu giao tiếp giữa vi điều khiển và C# qua chuẩn USB.
B. CHUẨN BỊ THÍ NGHIỆM
● Sinh viên đọc kỹ các yêu cầu của mỗi bài thí nghiệm, xem sơ đồ mạch trước khi lên
lớp.
● Sinh viên tìm hiểu cách sử dụng phần mềm STM32CubeMX, cách cài đặt thư viện
USB kết nối giữa vi điều khiển và máy tính.
C. GIỚI THIỆU MÔ HÌNH THÍ NGHIỆM
Kit thí nghiệm cho được bố trí như Hình 1
Sinh viên khai báo prototype các hàm ở phần USER CODE BEGIN PFP:
void USB_RX_Interrupt(void);
void Sample_Timer_Interrupt(void);
/* DI Function */
uint8_t DI_Read_All(void);
/* DO Function */
void DO_Init(void);
void DO_Write(int n, uint8_t DO_duty);
void DO_Write_All(uint8_t *DO_duty);
void DO_pwm_set_frequency(uint32_t *frequency);//KHz
/* AI Function */
void AI_Read_All(int *AI_value);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
/* AO Function */
void AO_Init(void);
void AO_Write(int n, float AO_value);
void AO_Write_All(float *AO_value);
/* Counter Function */
void Counter_Init(void);
uint32_t Counter_Read(void);
void Counter_Reset(void);
/* Sample Timer Function */
void Sample_Timer_Init(void);
void Sample_Timer_Set_Period(uint32_t Ts_ms);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim);
Tiếp đến sinh viên tìm đến hàm “static int8_t CUSTOM_HID_OutEvent_FS” ở gần cuối
và gọi hàm “USB_RX_Interrupt” trước return.
❖ Lập trình các hàm chức năng
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
Sinh viên lập trình tiếp ngay trong phần BEGIN 0 hoặc có thể kéo xuống dưới cùng file
main.c lập trình bên trong BEGIN 4.
● Nhóm hàm đọc tín hiệu (DI Function)
Sinh viên thực hiện viết bổ sung hoàn thiện (dựa trên ví dụ mẫu cho trước) hàm
“DI_Read_All” để đọc toàn bộ 8 chân tín hiệu Input. Chú ý, giá trị trả về của hàm này là một
biến duy nhất với kiểu dữ liệu số nguyên không dấu 8bit.
/* DI Function */
uint8_t DI_Read_All(void)
{
// Use one variable “DI_tmp” for all 8 DIs.
uint8_t DI_tmp = 0;
// Follow the example, students complete the function to read all the values
of 8 DIs.
return DI_tmp;
}
// Follow the examples, students complete the function to write all the values
of 8 DOs.
}
void DO_pwm_set_frequency(uint32_t *frequency)//KHz
{
// Frequency is an array with 3 elements
// Example for frequency changing for DO signal with PWM type
//if (frequency[0]) TIM15->ARR = …………;
// Follow the instruction, students complete the function for the remaining 2
frequencies
}
Trong hàm while, khoảng giữa CODE BEGIN 3, sinh viên lập trình đoạn chương trình
sau. Sinh viên tìm hiểu và giải thích ý nghĩa của toàn bộ đoạn code này.
if(usb_tx_flag)
{
usb_tx_flag =0;//Reset flag
uint8_t DI_value = 0;
uint32_t C0_value;
int AI_value[2] = {0,0};
break;
}
case 'F':/* pwm frequency */
{
uint32_t DO_pwm_frequency[3] = {0,0,0};
for(i=0;i<3;i++)
DO_pwm_frequency[i]= (uint32_t)(usb_rx_buffer[i+1]);
DO_pwm_set_frequency(DO_pwm_frequency);
break;
}
case 'T':/* sample time */
{
uint16_t Ts_ms = 1;
Ts_ms = ((int)usb_rx_buffer[1]<<8) + (int)usb_rx_buffer[2];
Sample_Timer_Set_Period(Ts_ms);
break;
}
default:
break;
}
}
Nhấn Ctrl + S để save chương trình, click vào biểu tượng trên thanh công cụ hoặc
nhấn F7 để biên dịch chương trình. Nếu biên dịch thành công, cửa sổ Build Output sẽ hiện thông
báo tương tự hình bên dưới.
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
Nhấn F8 hoặc nhấn vào biểu tượng để nạp chương trình xuống vi điều khiển. Sau
đó sinh viên tắt nguồn vi điều khiển bằng switch (bên cạnh đèn nguồn sáng ở giữa board, nhìn
hình 1)trên board rồi mở lại.
III. Bài thí nghiệm 3: Tạo window form app C#
❖ Tạo project mới:
Đặt thuộc tính Name của các textbox lần lượt là: txtProductName, txtVendorId,
txtProductId, txtManufacturer. Đặt thuộc tính Name của Button Connect là btnConnect.
- Khai báo thư viện
using LibUsbDotNet;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
- Khai báo biến toàn cục cho kết nối USB
namespace HIF__
{
public partial class Form1 : Form
{
#endregion variable
public Form1()
{
InitializeComponent();
}
❖ Chương trình con cần thiết để thiết lập kết nối và gửi nhận dữ liệu:
#region USB_DATA_RECEIVER_INIT
void USB_DATA_RECEIVER_INIT()
{
IUsbDevice wholeUsbDevice = myUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
wholeUsbDevice.SetConfiguration(1);
wholeUsbDevice.ClaimInterface(0);
}
// Open usb endpoint reader and writer
reader = myUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
writer = myUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
// Set Interrupt service routine for reader complete event
reader.DataReceived += (OnRxEndPointData);
reader.DataReceivedEnabled = true;
}
#endregion USB_DATA_RECEIVER_INIT
❖ Form Closed
Chọn vào sự kiện để viết đoạn chương trình đóng ứng dụng.
}
else
{
e.Cancel = true;
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
Usb_exit();
Application.Exit();
}
catch
{
}
}
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
Kéo một đối tượng Timer từ thanh Toolbox vào giao diện, đặt thuộc tính Name của Timer
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
là TimerSend.
Thay đổi thuộc tính Name của các đối tượng như sau:
- Checkbox ứng với Q0.0-Q0.7: chbDO0 - chbDO7
- Checkbox ứng với label PWM để chọn chế độ PWM hay GPIO: chbPWM0,
chbPWM1, chbPWM3, chbPWM6, chbPWM7.
- Textbox ứng với label Duty để cài đặt chu kì nhiệm vụ trong chế độ PWM, giá trị
nhập vào từ 0-100%: txtPWM0, txtPWM1, txtPWM3, txtPWM6, txtPWM7.
- Textbox ứng với label Frequency, giá trị nhập từ 1-255 KHz: txtF0, txtF1, txtF2.
- Button Config Frequency: btnConfigFreq.
- OvalShape để hiện thị DI0-DI7: ovalDI0- ovalDI7.
- Textbox hiển thị AI0-AI2: txtAI0, txtAI1, txtAI2.
- Textbox ứng với label Sample Time để đặt thời gian lấy mẫu trên cardUSB: txtTs.
Sinh viên tự viết đoạn chương trình tương tự để hiển thị DI1 – DI7.
- Hiển thị giá trị AIN và Counter
txtAI0.Text = AIValue[0].ToString("0.000");
txtAI1.Text = AIValue[1].ToString("0.000");
txtCounter0.Text = counterValue.ToString();
Sinh viên thay đổi giá trị trên các chân DI, AI, quay động cơ để thay đổi giá trị counter
và kiểm tra lại chương trình trên máy tinh có cập nhật những giá trị này.
Chương trình gửi sẽ được đặt trong ngắt Timer, ta config Timer và giá trị ban đầu của
các Textbox trong sự kiện Form1_Load.
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
Viết đoạn chương trình sau trong hàm ngắt timer1_Tick để cập nhật giá trị DO, AO:
● Cài đặt giá trị DO0:
if (DOInPWMMode[0]) // PWM 0
{
DOValue[0] = DODuty[0];
}
else // GPIO 0
{
if (chbDO0.Checked)
DOValue[0] = 100;
else
DOValue[0] = 0;
}
Sinh viên tự viết chương trình cài đặt giá trị cho tín hiệu DO1 – DO7.
● Gửi frame tới USBCard
byte[] data_send = { 14, (byte)'N', DOValue[0], DOValue[1],
DOValue[2], DOValue[3], DOValue[4], DOValue[5],
DOValue[6], DOValue[7], AO0ByteValue[0], AO0ByteValue[1],
AO1ByteValue[0], AO1ByteValue[1], resetCounterCmd };
USBWrite(data_send);
resetCounterCmd = 0;
Chú ý: Sinh viên kết hợp với kiểm tra checkbox PID Mode, nếu chọn chế độ này thì
không gửi các giá trị này cho vi điều khiển để tiện cho Bài thí nghiệm 4 ở dưới.
Tương tự sinh viên viết đoạn chương trình xử lý ngắt txtAO1_TextChanged
Sinh viên bật tắt DO trên máy tính và kiểm tra các LED DO trên USBCard. Nối chân
𝑉𝑜𝑢𝑡
AO1 với AI1, AO2 với AI2 trên panel và kiểm tra giá trị 𝑉𝐴𝐼𝑁 =
2
Tương tự sinh viên thực hiện cho các sự kiện của các Checkbox chọn DO còn lại.
Ta viết đoạn chương trình sau trong hàm ngắt txtPWM0_TextChanged
if (txtPWM0.Text == "")
{
DODuty[0] = 0;
}
else
{
Int16 DODutyTmp = Convert.ToInt16(txtPWM0.Text);
if (DODutyTmp < 0 || DODutyTmp > 100)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 0 đến 100",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
DODuty[0] = (byte)(DODutyTmp);
}
}
Tương tự sinh viên thực hiện cho các Textbox của PWM. Sinh viên thay đổi số trong
trong Textbox PWM và quan sát độ sáng đèn DO trên USBcard.
Ta viết đoạn chương trình trong hàm ngắt txtF0_TextChanged
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
if (txtF0.Text == "")
{
MessageBox.Show("Hãy nhập giá trị từ 1 đến 255", "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
Int16 FValueTmp = Convert.ToInt16(txtF0.Text);
if (FValueTmp < 1 || FValueTmp > 255)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 1 đến 255",
"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
FValue[0] = (byte)FValueTmp;
}
}
Tương tự sinh viên viết tiếp cho các sự kiện txtF1_TextChanged, txtF2_TextChanged
Viết chương trình xử lý sự kiện nhấn nút Config Frequency, Config Ts và Reset Counter
private void btnConfigFreq_Click(object sender, EventArgs e)
{
byte[] data_send = { 4, (byte)'F', FValue[0], FValue[1], FValue[2] };
USBWrite(data_send);
}
private void btnTs_Click(object sender, EventArgs e)
{
byte[] data_send = { 3, (byte)'T', 0, 0 };
data_send[2] = (byte)(TsValue >> 8);
data_send[3] = (byte)(TsValue);
USBWrite(data_send);
}
private void resetC0_Click(object sender, EventArgs e)
{
resetCounterCmd = (byte)'R';
}
Thay đổi thời gian lấy mẫu lần lượt là 1000ms, 10ms và quan sát tốc độ thay đổi giá trị
trong textbox AIN. Quay động cơ về vị trí 0 và nhấn Reset Counter, kiểm tra giá trị counter.
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
IV. Bài Thí nghiệm 4: Điều khiển vị trí động cơ bằng bộ điều khiển PID
Trong bài thí nghiệm này, ta sử dụng cầu H BTS7960 để điều khiển động cơ DC động cơ.
Nguyên lý hoạt động của cầu H BTS7960:
- PULSE1 = 1 và PULSE2 = 0: động cơ quay thuận
- PULSE1 = 0 và PULSE2 = 1: động cơ quay nghịch
- Cả 2 PULSE bằng 0 hoặc 1: động cơ dừng lại.
Chú ý: để điều khiển động cơ, trên panel ta nối chân PLS1, PLS2 với PLS1, PLS2 trên
mô hình động cơ. Trên động cơ có gắn nối tiếp Enocder nhằm xác định vị trí của động cơ. Sinh
viên cần nối 2 ngõ ra A và B trên động cơ tương ứng với A và B trên panel để vi điều khiển đọc
tín hiệu Encoder. Sinh viên không nối dây vào các đầu nối màu đen bên dưới vì những chỗ
này đã được nối sẵn với các chân tín hiệu ở vi điều khiển.
Sinh viên nối 2 dây nguồn 5V và 12V tương ứng và 1 dây GND bên mô hình động cơ
với panel để cấp nguồn cho động cơ và Encoder.
Cài đặt giá trị PWM0, PWM1 là 1kHz, thời gian lấy mẫu của USB card là 10ms. Viết
thêm đoạn chương trình gửi giá trị điều khiển xung PWM sau vào cuối hàm ngắt nhận
UsbReceiverActionFunction.
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
/* CALCULATE PID */
currentPos = counterValue;
// Write your code here to calculate pidOutput from currentPos
if (pidOutput > 0)
{
data_send[2] = (byte)pidOutput;
}
else
{
pidOutput = -pidOutput;
data_send[3] = (byte)pidOutput;
}
USBWrite(data_send);
resetCounterCmd = 0;
Chú ý: sinh viên kết hợp kiểm tra điều kiện checkbox PID mode.
Sinh viên tự tạo các textbox để đặt giá trị cần điều khiển, các thông số của bộ điều khiển
PID và bổ sung chương trình tính giá trị PID vào đoạn chương trình ở trên dựa trên công thức
đã cho ở dưới.
1 𝐾𝑑
𝑢(𝑘) = 𝐾𝑝 𝑒(𝑘) + 𝐾𝑖 𝑇𝑠 (𝑒(𝑘) + 𝑒(𝑘 − 1)) + (𝑒(𝑘) − 2𝑒(𝑘 − 1) + 𝑒(𝑘 − 2))
2 𝑇𝑠
Nội dung Báo cáo thí nghiệm:
- Giải thích chương trình trên USBCard và máy tính trong các thí nghiệm 1 và 2.
- Báo cáo chương trình PID điều khiển vị trí động cơ.
Bài TN4: Lập trình card USB Biên soạn: TS. Nguyễn Trọng Tài
Bảng nhận xét, đánh giá của GV
Thông tin Sinh viên:
Nhóm:
Họ và Tên SV:
MSSV:
Các nhiệm vụ và nội dung thực hiện trong buổi thí nghiệm:
REFSD+ 23
9 VDDA/VREF+ VSSA/VREF- 8
STM32F373CCT6 3B
1 LM358
I3 6 OUT_2 5
VIN+ SDA 4 SDA 1 R10 2 AO2
AI4
VIN- SCL 3 SCL 1
V
DDA 5 VDD VSS 2
6R
1
R12 2
GND