Technology sharing

STM32-I2C

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Hoc contentus fundaturJiangxie Technology STM32Composuit post studium vide.

1. I2C communicationis

1.1 Introductio ad I2C communicationem

  • I2C (Inter IC Bus) notitia universalis a Philips evoluta est
  • Lineae communicationis duae: SCL (Serial Horologium) Vide horologium, SDA (Serial Data) Vide data linea
  • Synchronum, dimidium duplex, simplex finitum, multi-machinam
  • Respondeo cum data
  • Subsidiis iniciendis machinis in bus multiplex (unus dominus et multi servi, multi domini et multi servi)
    • Unus dominus, multi servi: Microcontroller militat et dominatur operationi I2C bus. Omnes moduli externi in I2C bus inclusi sunt servi sine venia.
    • Multi-dominus et multi-servus: quilibet modulus in bus activum salire potest et agere ut dominus. Cum bus conflictus incidit, I2C protocollum arbitrium agendi habebit.

image.png

1.2 hardware circuit

  • Omnium SCL I2C machinis connexis, et SDA connexis.
  • Ambo SCL et SDA de fabrica configurari debent in modo sicco aperto.
  • Resistentem viverra sursum singulis SCL et SDA adde, valor resistentiae plerumque circiter 4.7KΩ

figure 1figure 2
image.png

  • Dominus unus et plures servi: CPU microcomputer unus est. Praeterea in otiosis, hospes actuose imperium SDA inchoare potest. Solus cum servus data mittit et servus respondet, hospes SDA imperium in servum transferet.
  • Moderata IC servus insidens in I2C bus, qui habitus esse potest sensorem, OLED, memoriam, horologium moduli, etc. Potestas servi est relative parva. Data linea SDA, servus active inchoare non licet imperium SDA. Solus dominus post mandatum mittit ut servum legere, vel cum servus responderit, servus SDA breviter potiri potest.
  • Figura 2: SCL ad sinistram, SDA ad dextram. Omnia data inputare possunt per quiddam vel Schmitt felis.
    • Quia input in circuitu nullum effectum habet, aliqua machina input aliquando habere potest.
    • Output output output-patens adoptat configuration non cohaeret cum aliquo et in statu fluitantis est, ita ut omnes cogitationes non possint nisi humili gradu, sed non altam bus, per resistor. Hoc modo, primo, phaenomenon potentiae brevitatis ambitus omnino eliminat et in tuto circumitu tutatur; In modum exhaurire aperta, outputare altam gradum disiungendi clavum aequipollet, sic directe outputare potes altam gradum antequam inputting. Tertius hic modus "wired AND" phaenomenon habet. Quamdiu quis vel plures machinas in humili gradu output habet, in humili gradu tantum est. . Ergo I2C uti potest huius phaenomeni ad horologium synchronizationem et compromissum in multi-domino modo agendi. Quamvis SCL hic uti possit in uno domino et pluribus servis modum dis-trahere output, eo tamen utitur in aperto plus extrahendi output modum.

1.3 I2C leo basic unitas

1.3.1 Satus conditionibus et ending conditionibus

  • incipiens conditione: Per SCL altam, SDA permutat ab alto gradu ad humilem gradum
  • Terminatio conditio: Per SCL altam, SDA permutat ab humili gradu ad altam

image.png

  • sub conditionibus initial : Cum I2C bus in statu otioso, tam SCL et SDA sunt in alta statu, hoc est, nulla fabrica tangit SCL et SDA in statu quieto. Cum necesse exercitum mittere et data accipere, primum debet rumpere silentium bus et generare principium conditionis, id est, SCL in altiori gradu, non tangendo, et deinde SDA descendens ad generale labium trahere. Cum servus SCL altam gradum capit et signum ore SDA cadens, se retexere et vocati domini exspectabunt. Post aciem SDA cadentem, hospes iterum SCL destruere debet. In altera parte, bus occupat, et ab altera parte, etiam faciliorem evolutionem unitatis fundamentalis. Postea providebitur quod, praeter initium et finem conditionum, uniuscuiusque sequentialis unitas SCL incipit ab humili gradu et terminatur cum humili gradu.
  • In terminatione conditionis status : SCL primum dimittit et ad altam redit, deinde SDA dimittit et resilit in altum gradum, ore ortum generans, qui condicionem terminat. Post condicionem simultaneam terminationem, tum SCL et SDA altae sunt et ad statum tranquillitatem initialem revertuntur.
    Satus et desine generantur ab hospite, et servus incipere et desinere generare non licet. Ergo, cessante bus otiosis, debet servus manus suas semper dimittere, et non licet prosilire et bus tangere.

1.3.2 mitte byte

  • Mitte ad byte: In humili gradu SCL , hospes in SDA lineas notitias ponit in ordine (altum primum), et tunc emittit SCL habere aliquem datam in alto gradu SCL.

Hospes humiles ponit data, et summus gradus legit servus data.
image.png
Post initium conditionis, primum byte etiam ab hospite mitti debet. Cum SCL humilis est, si vult exercitum mittere 0, vellit SDA humilis; In gradu humili SCL, gradu SDA mutare permittitur. Post data data, hospes emittit lineam horologii et SCL ad altam redit. Per altam, tempus est cum servus legit SDA, ita per altam, SDA mutare non licet. Postquam SCL in alta est, servus debet legere SDA quam celerrime. Quia horologium a domino regitur, servus nescit cum in ore cadens incidit, sic servus datas leget in ore SCL ortu. Postquam hospes SCL per spatium temporis dimittit, SCL humilem trahere et sequentem frenum transmittere pergere potest. Exercitus etiam notitias in SDA quam primum post cadentem aciem SCL imponere debet. Sed hospes in horologio potestatem habet, ideo solum opus est ut aliquando SDA data in humili gradu humilitatis posita sit. Post data dimissa, hospes iterum SCL dimittit, SCL altus est, et servus hunc frenum legit. Loop hoc processum: hospes SCL humilem trahit, notitia in SDA ponit, hospes SCL dimittit, et servus data SDA legit. Sub synchronisation of SCL, dominus transmittit et servus in ordine accipit.
Cum sit summus ordo bis primum, primum frenum est summum B7 of byte, et infimum B0 mittitur novissimum.

1.3.3 accipite byte

  • byte recipere: In humili gradu SCL, servus in linea SDA data frena ponit in ordine (altum primum), et tunc emittit SCL In altiori gradu SCL datas habere.

Servus humilis gradu ponit data, altam exercitum legit data
image.png
SDA linea: Dominus debet dimittere SDA antequam accipiat. Hoc tempore servus imperium SDA obtinet resilit altam. Iumi data conversis, altam legit data. Linea solida dominum moderatum significat, et linea punctata aequalem servum gubernat. SCL per totum processum exercitum refrenat, et SDA hospes dimitti ante recipiendum et tradendum servo in potestatem. Quia horologium SCL ab hospite refrenat, notitia conversionis servi plerumque in ore SCL cadens exercetur, et exercitus aliquando legi potest quando altus est SCL.

1.3.4 Mitte responsum et accipere responsum

  • Mitte responsum: Recepta byte, hospes unum frenum of notitia in proximam horologium mittit.
  • accipere responsum: Post exercitum byte mittit, accipit aliquid de notitia in proximo horologio ut utrum servus respondeat.

image.png
Id est, cum leo mittendi byte vocatum est, ex eo sequendus est timidus vocandi, qui responsum accepit, quo usus est, utrum servus datam sibi datam acceperit. Si servus acceperit, tunc respondetur frenum, cum dominus dimiserit SDA, servus statim descendet SDA, et tunc in alta gradu SCL, exercitum legit responsum frenum. Si autem responsum est 0, servus quidem accepit.
Cum byte accipiendo, responsum mittere debes. Responsum mittendi propositum est servo nuntiare num mittens pergere vis. Si servus responsum a domino receperit, praemisso nummo datae, servus apparatus mittere pergit. Si apparatus servus responsum a domino machina non acceperit, servus machinae partem datae aestimabit Missus est, sed dominus machina me negligit. Forsitan Exercitus non vult. Hoc tempore servus oboedienter SDA dimittet et SDA potestatem tradet ne impedimentum cum operationibus subsequentibus hospitio recipiat.

1.4 I2C leo

1.4.1 det speciem oratio scribere

  • Specificare oratio scribere
  • Pro certo artificio (Inscriptio Serva) certa notitia scribe (Inscriptio) ad certam electronicam (Reg Address) (id est electronica inscriptio certa machinae)

image.png
processus:
(I) Satus conditionibus
(II) Timing mittendi byte-0xD0 (servus oratio (7bit) + scribe (1bit)-0) (1101 0000)
(3) Accipe responsum: RA = 0 (accipe responsum a servo)
(4) Certa oratio: 0x19 (0001 1001)
(5) Responsum accipe: RA = 0 (accipe responsum a servo)
(VI) certa notitia Write: 0xAA (MX MX)
(7) Accipe responsionem: RA = 0
(8) Desine frenum P (condicione terminatio).

  • Post condicionem satus, leo byte mittere debet. Contentum byte debet esse oratio servus + legere et scribere frena 8 Tibul. Oratio servum mittere est obiectum communicationis determinet, ac mittens legere et scribere aliquid confirmare est num scribere vel legere proximus. Nunc exercitus mittit partem datam. Contentum byte convertitur ad hexadecimalem. Princeps ordo frenum praecedit primum, quod est 0xD0. finit scribendum bit, et SCL evulsum Z. Post, hospes SDA dimittere debet, sequitur in agnos- bit RA.
  • In alta gradu post responsum frenum RA finit generatur servus solvens SDA cadens margo SCL eodem fere tempore.
  • Post completam responsionem, si unum byte mittere pergas, secundus byte mitti potest ad machinam designatam. Generaliter, secundus byte potest esse electronica inscriptio seu instructio dominii verbi, etc., et tertia byte est contentum quod hospes inscriptionem electronicam scribere vult (secundus byte).
  • P sistendum est bit.

Propositum huius notae tabulae est: ad fabricam quae inscriptio servi 1101000 specificat, scribe notitia 0xAA in suo interno tabulario ad electronicam 0x19.
0 significat: exercitum perficiet operationem scribe in leo subsequente;
1 significat: militiam operationem in subsequenti leo serie perficiet;

1.4.2 Current inscriptio Lectio

  • Current inscriptio legere
  • Pro certo artificio (Inscriptio ancillae), data servi lege (Indicium) in inscriptione indicata a monstrante currente inscriptionem.

image.png
processus:
(I) Satus conditionibus
(II) Timing mittendi byte-0xD1 (servus oratio (7bit) + lege (1bit)-1) (1101 0001)
(3) Accipe responsum: RA = 0 (accipe responsum a servo)
(4) Lege servi notitia: 0x0F (0000 1111)
(7) Mitte responsionem: SA = 0
(8) Desine frenum P (condicione terminatio).

  • Quod legere et scribere frenum est 1, indicans operationem sequentem legere opus esse. Servus post respondet (RA=0), directio data transmissionis inversa erit. Dominus potestatem SDA servo tradere vult, et dominus leo recipiendi byte ad recipiendam operationem vocat.
  • In secundo byte, servus licentiam a domino accipit, et SCL in humili gradu SCL scribere potest VIII frusta, byte data a servo misso, recipitur, quod est 0x0F. Sed quo servi mandare est 0x0F? In lectione sincere, in I2C protocollo cavetur ut, cum hospes alloquitur, statim lege et scribe vexillo 1 apponatur. Proxima byte statim transibit ut leo legeretur. Igitur hospes incipiet accipere antequam tempus habeat ut specificare quae subcriptio legere vult, ideo nulla pagina hic inscriptionis specificare potest. In machina servili, omnes tabulae ad aream linearem partita sunt, et regulae variae singulae erunt indicans unum e registris Lectis byte, monstrator statim incremento semel et in proximum locum movebit. Deinde cum leo recitans currentem electronicam vocans, si hospes non specificat quam electronicam ad legendum sit, servus redibit in tabulario a monstrato. monetae pretii.

1.4.3 Read in certa oratio

  • Specificare oratio legere
  • Ad certa machinam (inscriptio servus) sub certa inscriptione (Reg Oratio) data servus lege (Indicium)

image.png
Incipere primum, deinde incipere, deinde desinere
processus:
(I) Satus conditionibus
(II) Timing mittendi byte-0xD0 (servus oratio (7bit) + scribe (1bit)-0) (1101 0000)
(3) Accipe responsum: RA = 0 (accipe responsum a servo)
(4) Certa oratio: 0x19 (0001 1001)
(5) Responsum accipe: RA = 0 (accipe responsum a servo)
(VI) iterare condicionem incipiens
(7) Timing mittendi byte-0xD1 (inscriptio ancillae (7bit) + lege (1bit)-1) (1101 0001)
(8) Accipe responsum: RA = 0
(9) Lege notitia servi: 0xAA (1010 1010);
(10) Mitte responsionem: SA = 0
(11) Siste bit P (condition terminatio).

  • Prima pars est scribere ad certam inscriptionem, sed solum oratio specificata est, et tempus scribendi nullum est; secunda pars legit currentis inscriptionis, quia oratio modo designata est, ergo inscriptio hodierna legitur iterum vocavit.
  • Inscriptio ancillae specificatae 1101000 est, signum scribe-0 est, et operatio scribe exercetur. Postquam servus respondet, alius byte (secundus byte) scriptus est, ut servo electronica scribatur inscriptionem electronicam, hoc est, postquam servus notitias accipit, ejus tabularii regula demonstrat positionem 0x19.
  • Sr iterata condicio est, quae aequivalet ut novum leo incipiens. Quia signum certum legere et scribere non potest nisi primam conditionem initii byte sequi, ergo si vis directionem legere et scribere, solum habere potes. aliam conditionem incipere.
  • Tum post condicionem inceptam, re-inscriptio et signum lecti vexillum scribe hoc tempore. In hoc tempore, scribe frenum vexillum est 1, significans legendum esse 0xAA in inscriptione 0x19.

2. MPU6050

2.1 Introductio ad MPU6050

  • MPU6050 est habitus 6-axis sensor qui accelerationem et velocitatem angulares parametri sui X, Y, Z axium metiri potest vehiculis, aircraft, etc. quae se deprehendere necesse est
  • 3-axis accelerometer.
  • 3-axis gyro sensoris (Gyroscope): velocitatem angularis mensurarum axium X, Y, Z.

image.png

  • In aircraft fuselage exempli gratia, angulus Euler angulus est inter fuselagem aircraft et tres initiales axes.
    • scaphaIn nasum elit insultantem vel sursumangulus inter hunc axem appellaturPix
    • scaphaIn fuselage rotulis ad sinistram vel dextramangulus inter hunc axem appellaturRoll
    • scaphaCustodi fuselage graduVertere nasum aircraft ad sinistram vel dextramangulus inter hunc axem appellaturYaw
    • Angulus angulus Euler habitus hoc tempore aircraft repraesentat, sive sursum sive deorsum est, sinistrorsum sive dextrorsum iugo.
  • Communes notitiae fusione algorithmorum plerumque eliquationem complementariam includunt, Kalman eliquare, etc., rationemque habitus in navigatione inertiae.
  • Accelerometer : Linea in medio punctata est inductio axis. Cum lapsus moveatur, potentiometer in eo movebit. Hoc potentiometer est voltage resistor dividens. Hoc accelerometer actu fons dynamometer. Accelerometer est in singulis axibus X, Y, Z. Accelerometrae stabilitatem habent stabilem sed non stabilitatem dynamicam.
  • Gyro sensorem : In medio rota rotata cum quadam mole. Cum rota gyra magna celeritate gyratur, secundum principium conservationis momenti angularis, rota rotata tendit ad suum momentum originale conservandum directio rotationis axis immutatus. Cum directio objecti externi gyratur, directio rotationis internae non gyratur, quae in nexu anuli angularis declinationem efficiet. Si potentiometer in connexione rotato pones et intentionem potentiometri metiaris, angulum rotationis obtinere potes. Gyroscopum directe obtinere posset angulum, gyroscopus autem huius MPU6050 angulum directe metiatur. -axis. Integrum velocitatis angularis est angulus. Cum autem obiectum est stationarius, valor velocitatis angularis ad nihilum ob sonitum penitus reverti non potest. qui est angulus per integrationem velocitatis angularis. Non potest sustinere temporis spatium, sed hic angulus haud dubium est an sit immotus vel movens, nec a motu objecti afficiatur. Gyroscopia dynamicam stabilitatem habent, non stabilitatem stabilitatem.
  • Secundum accelerometrum, quod stabilitatem habet sed stabilitatem dynamicam non habet; gyroscopus dynamicam habet stabilitatem sed stabilitatem non habet , inconcinnus est habitus tam stabilis quam dynamicus.

2.2 MPU6050 parametri

  • 16-bit ADC colligit signum analogicum sensoris, range quantitatis: -32768~ 32767
  • Accelerometer plenus-scalae lectio: ±2, ±4, ±8, ± 16 (g) (1g = 9.8m/s2)
  • Gyroscopia plena lectio: ± 250, ± 500, ± 1000, ± 2000 (°/sec, gradus/ secundus, velocitas angularis unitas, quot gradus rotationis per alterum) (maior plena lectio, latior Mensuratio minor.
  • Configurable digital low-pass colum: Tabularium configurari potest eligere humilem transitum eliquandi notitiarum output.
  • Configurable horologium fons
  • Configurable sampling frequency division: Horologiorum fons per frequentiam divisorem dividi potest ut horologiorum AD conversionem et alios circuitus interni praebeat. Coaefficientem divisionem frequentiam moderans, celeritatem AD conversionis regere potes.
  • I2C inscriptio ancillae: 1101000 (AD0=0) vel 1101001 (AD0=1)
    • 110 M convertitur in hexadecimale, quod est 0x68, ut quidam dicunt MPU6050 servus inscriptio est 0x68. Sed in communicatione I2C, princeps 7 frusta primi byte sunt servi inscriptio, et infima frenum est legere et scribere frenum 0x68 Shift reliquit per 1 partem (0x68 << 1), deinde lege et scribe particulas per bitwise vel sursum, lege 1 et scribe 0.
    • Alius modus est notitias 0x68 in partem sinistram per 1 partem transferre (0x68 << 1) ut servus inscriptionis, quae est 0xD0. In hoc casu, oratio servi MPU6050 est 0xD0 Hoc tempore, cum actu primum byte mitto, si scribere vis, tantum utere 0xD0 ut primum byte; . Haec repraesentatio non requirit sinistram mutationem operationis, vel aliis verbis, haec repraesentatio in servili inscriptione frenos legere et scribere integrat. 0xD0 est inscriptio electronica scribe et 0xD1 est inscriptio lecta.

2.3 hardware circuit

image.png

pinOfficium
VCC、GNDpotentia copia
SCL、SDAI2C communicationis pin
XCL、XDAHostia I2C communicationis paxilli
AD0Servus frenum in infima inscriptione
INTInterpellare signum output
  • LDO: humilis dropout voltage linearis ordinator, 3.3V ordinator intentionis.
  • SCL et SDA: I2C sunt fibulae communicationis. Modulus resistentibus in duobus 4.7K extruxit, ut cum wiring, mox coniunge SDA et SCL directe ad portum GPIO .
  • XCL, XDA: Hostiae I2C acus communicatio. Hae duae fibulae ad functiones chip ampliandas destinantur. Solet pro magnetometris externis vel barometris adhibitis. Cum haec dilatatio astulae coniunctae sunt, exercitus instrumenti MPU6050 directe accedere potest notitia harum expansionis astularum et datas expansionis astulas in MPU6050 legas habitus calculi.
    AD0 paxillus: Inscriptio servi infima est. Si cum humili gradu coniungitur, 7-frenum ancillae inscriptio 1101000 est; Resistens est in diagrammate ambitu, quod aegre deorsum per defaltam deprimitur, ergo si clavus fluitans relinquitur, planum est humile et evellere fortiter ad altam.
  • INT: Interrumpe output acus. Configurare potes aliquos casus intra chip ut felis output clavum interruptum, ut notitia parata, I2C exercitum errorem, etc.
  • Chip quoque constructum est in: gratuita detectio ruinae, motus detectio, motus detectio nulla, etc. Haec signa felis INT clavum ad generandum transitum aequalem, signa interrumpere possunt, si opus est, configurari.
  • Potestas copia chip MPU6050 est 2.375-3.46V, quae est a 3.3V fabrica vis copiarum et cum 5V coniungi non potest. Ergo ordinator intentionis 3.3V additur, et initus terminalis intentionis VCC_5V potest esse inter 3.3V et 5V , virtutis indicator lumen accendet.

2.4 MPU6050 obstructionum diagram

image.png

  • CLKIN et CLKOUT sunt horologii initus paxilli et horologii output fibulae, sed plerumque horologium internum utimur.
  • Pars grisea: est sensorem in medio chip, accelerometer in XYZ, et gyroscopus in axe XYZ.
  • Est etiam in sensori temperato constructo quod ad temperaturam metiendam adhiberi potest.
  • Hi sensores essentialiter aequipollent repugnantibus variabilibus. Post dividentes intentionem, analogam intentionem constituunt, ac deinde conversionem analogicam-digitalem per ADC complent mandare, quod obtineri potest ex registro notitiarum legendo. Omnes conversiones in hoc spumae plene automantur.
  • Uterque sensorem sui testem unitatis habet, quae qualitatem capitis cognoscere adhibetur solito maior. Processus experimenti sui: Potes seipsum experiri primum, data lege, deinde sui ipsius experimentum, datam lege, duas notitias subtrahe, et inde data responsio propria test. Ad hanc responsionem sui probationis, manus manus dat.
  • Crimen Pump: Praesent laoreet mollis lacus vel laoreet.
  • Acus CPOUT capacitorem externum requirit.
  • Status registri interrumpere: moderari potest quem eventus interni sunt output ad paxillum interrumpendum;
  • FIFO: Primum-in-primo-ex registro, qui e notitia amnis condire potest.
  • Configurationis mandare: varios ambitus internos configurare potes
  • Sensorem mandare: Data mandare, quae notitias cuiusque sensoris addit.
  • Factory Calibrated: Id est quod sensoriis intus calibrantur.
  • Processus motus digitalis: DMP pro brevi, est algorithmus ferramentorum ad calculi habitus, qui intra chip.
  • FSYNC: Artus synchronisation.

3. 10-1 Software I2C legendi et scribendi MPU6050

3.1 Hardware iunctio

Per programmatum I2C communicatio, tabulas intra MPU6050 scribes et scribe. scribendo ad tabulam configurationis, obturaculum in modulo configurare potes In OLED lecta notitia ostendetur. Summa data est ID numerus machinae. Infra, tres a sinistris sunt outputa data accelerationis sensoris, quae sunt acceleratio axis X-axis, Y-axis et Z-axis respective. quae sunt velocitas angularis axis X-axis Y-axis & Z-axis.
SCL cum PB10 clavo STM32 coniungitur, et SDA cum PB11 paxillo coniungitur. Cum planum programmatum flipping hic perficiatur, duo portus GPIO pro voluntate coniungi possunt.

3.2 Proventus Operatio

IMG_20240406_155156.jpg

3.3 Code flow

STM32 hospes est et MPU6050 servus est, qui modus domini est.

  1. Constitue .c et .h modulorum I2C stratorum communicationis
    1. GPIO initialization de I2C underlying scribe:
    2. 6 unitates fundamentales leo: initium, finis, byte mitte, byte accipias, responsionem mitte, responsionem suscipe
  2. Creare .c et .h modulorum MPU6050
    1. Ex modulo communicationis I2C, id efficit ut legentem in certa inscriptione, scribens in electronica certa, libros scriptos ut spumam configuraret, et libros legeret ut data sensorem obtineat.
  3. main.c
    1. Voca moduli MPU6050, initialize, datam obtine, et datam ostende

3.4 Code

  1. I2C codicem:
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10,(BitAction)BitValue);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void)
{
/*
软件I2C初始化:
	1. 把SCL和SDA都初始化为开漏输出模式;
	2. 把SCL和SDA置高电平;
输入时,先输出1,再直接读取输入数据寄存器就行了;
初始化结束后,调用SetBits,把GPIOB的Pin_10和Pin_11都置高电平,
此时I2C总线处于空闲状态
*/	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
	
}

/*
起始条件:SCL高电平期间,SDA从高电平切换到低电平。
如果起始条件之前,SDA和SCL都已经是高电平了,那先释放哪一个是一样的效果。
但是这个Start还要兼容重复起始条件Sr,Sr最开始,SCL是低电平,SDA电平不敢确定,
所以为保险起见,在SCL低电平时,先确保释放SDA,再释放SCL。
这时SDA和SCL都是高电平,然后再拉低SDA、拉低SCL。
这样这个Start就可以兼容起始条件和重复起始条件了。
*/
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

/*
终止条件:SCL高电平期间,SDA从低电平切换到高电平
如果Stop开始时,SCL和SDA都已经是低电平了,那就先释放SCL,再释放SDA。
但在这个时序单元开始时,SDA并不一定是低电平,所以为了确保之后释放
SDA能产生上升沿,要在时序单元开始时,先拉低SDA,然后再释放SCL、释放SDA。
*/
void MyI2C_Stop(void)// 终止条件
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

/*
发送一个字节:发送一个字节时序开始时,SCL是低电平。
除了终止条件SCL以高电平结束,所有的单元都会保证SCL以低电平结束。
SCL低电平变换数据;高电平保持数据稳定。由于是高位先行,所以变换数据的时候,
按照先放最高位,再放次高位,...,最后最低位的顺序,依次把每一个字节的每一位放在SDA线上,
每放完一位后,执行释放SCL,拉低SCL的操作,驱动时钟运转。
程序:趁SCL低电平,先把Byte的最高位放在SDA线上,
*/

void MyI2C_SendByte(uint8_t Byte) // 发送一个字节
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));// 右移i位
	    MyI2C_W_SCL(1);
	    MyI2C_W_SCL(0);
	}
}

/*
接收一个字节:时序开始时,SCL低电平,此时从机需要把数据放到SDA上,
为了防止主机干扰从机写入数据,主机需要先释放SDA,释放SDA相当于切换为输入模式,
那在SCL低电平时,从机会把数据放到SDA上,如果从机想发1,就释放SDA,想发0,就拉低SDA,
主机释放SCL,在SCL高电平期间,读取SDA,再拉低SCL,低电平期间,从机就会把下一位数据放到SDA上,重复8次,
主机就能读到一个字节了。
SCL低电平变换数据,高电平读取数据,实际上是一种读写分离的操作,低电平时间定义为写的时间,高电平时间定义为读的时间,

*/
uint8_t MyI2C_ReceiveByte(void) // 接收一个字节
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1); // 主机读取数据
	    if (MyI2C_R_SDA() == 1) // 如果if成立,接收的这一位为1,
	    {
		    Byte |= (0x80 >> i);   // 最高位置1
	    }
        MyI2C_W_SCL(0);	
	}
	return Byte;
}
/*
问题:反复读取SDA,for循环中又没写过SDA,那SDA读出来应该始终是一个值啊?
回答:I2C是在进行通信,通信是有从机的,当主机不断驱动SCL时钟时,
从机就有义务去改变SDA的电平,所以主机每次循环读取SDA的时候,
这个读取到的数据是从机控制的,这个数据也正是从机想要给我们发送的数据,
所以这个时序叫做接收一个字节。
*/

void MyI2C_SendAck(uint8_t AckBit) // 发送应答
{
	// 函数进来,SCL低电平,主机把AckBit放到SDA上,
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);  // 从机读取应答
	MyI2C_W_SCL(0);  // 进入下一个时序单元
	
}

uint8_t MyI2C_ReceiveAck(void) // 接收应答
{
	// 函数进来,SCL低电平,主机释放SDA,防止从机干扰
	uint8_t AckBit;
	MyI2C_W_SDA(1);  // 主机释放SDA
	MyI2C_W_SCL(1);  // SCL高电平,主机读取应答位
	AckBit = MyI2C_R_SDA(); 
	MyI2C_W_SCL(0);	 // SCL低电平,进入下一个时序单元
	return AckBit;
}

/*问题:在程序里,主机先把SDA置1了,然后再读取SDA,
这应答位肯定是1啊,
回答:第一,I2C的引脚是开漏输出+弱上拉的配置,主机输出1,
并不是强制SDA为高电平,而是释放SDA,
第二,I2C是在通信,主机释放了SDA,从机是有义务在此时把SDA再拉低的,
所以,即使主机把SDA置1了,之后再读取SDA,读到的值也可能是0,
读到0,代表从机给了应答,读到1,代表从机没给应答,这就是接收应答的流程。


*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  1. Code MPU6050:
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

// 宏定义: 寄存器的名称   对应的地址

#define	MPU6050_SMPLRT_DIV		0x19  // 采样率分频
#define	MPU6050_CONFIG			0x1A  // 配置外部帧同步(FSYNC)引脚采样和数字低通滤波器(DLPF)设置
#define	MPU6050_GYRO_CONFIG		0x1B  // 触发陀螺仪自检和配置满量程
#define	MPU6050_ACCEL_CONFIG	0x1C  // 触发加速度计自检和配置满量程

#define	MPU6050_ACCEL_XOUT_H	0x3B  // 存储最新的加速度计测量值
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41  // 存储最新的温度传感器测量值
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43  // 存储最新的陀螺仪测量值
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B  // 电源管理寄存器1
#define	MPU6050_PWR_MGMT_2		0x6C  // 电源管理寄存器2
#define	MPU6050_WHO_AM_I		0x75  // 用于验证设备身份

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"

// 宏定义:从机地址
#define MPU6050_ADDRESS  0xD0

// 指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址后,接收应答
	MyI2C_ReceiveAck();// 寻址找到从机之后,继续发送下一个字节
	MyI2C_SendByte(RegAddress); // 指定寄存器地址,存在MPU6050的当前地址指针里,用于指定具体读写哪个寄存器
	MyI2C_ReceiveAck();
	MyI2C_SendByte(Data);// 指定写入指定寄存器地址下的数据
	MyI2C_ReceiveAck();
	MyI2C_Stop();
}

// 指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress); // 指定地址:就是设置了MPU6050的当前地址指针
	MyI2C_ReceiveAck();
	// 转入读的时序,重新指定读写位,就必须重新起始
	MyI2C_Start();// 重复起始条件
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);// 指定从机地址和读写位,0xD0是写地址,或上0x01变为0xD1,读写位为1,接下来要读从机的数据
	MyI2C_ReceiveAck(); // 接收应答后,总线控制权就正式交给从机了,从机开始发送一个字节
	Data = MyI2C_ReceiveByte();// 主机接收一个字节,该函数返回值就是接收到的数据
	// 主机接收一个字节后,要给从机发送一个应答
	MyI2C_SendAck(1);// 参数为0,就是给从机应答,参数给1,就是不给从机应答
	// 如果想继续读多个字节,就要给应答,从机收到应答之后,就会继续发送数据,如果不想继续读了,就不能给从机应答了。
	// 主机收回总线的控制权,防止之后进入从机以为你还想要,但你实际不想要的冲突状态,
	// 这里,只需要读取1个字节,所以就给1,不给从机应答,
	MyI2C_Stop();
	return Data;
}

void MPU6050_Init(void)
{
	MyI2C_Init();
	// 写入一些寄存器对MPU6050硬件电路进行初始化配置
	// 电源管理寄存器1:设备复位:0,不复位;睡眠模式:0,解除睡眠:循环模式:0,不循环;无关位i:0;温度传感器失能:0,不失能;最后三位选择时钟:000,选择内部时钟,001,选择x轴的陀螺仪时钟,
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);// 解除睡眠,选择陀螺仪时钟
	// 电源管理寄存器2:前两位,循环模式唤醒频率:00,不需要;后6位,每一个轴的待机位:全为0,不需要待机;
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00); // 均不待机
	// 采样率分频:该8位决定了数据输出的快慢,值越小越快
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);// 采样分频:10分频
	// 配置寄存器:外部同步:全为0,不需要;数字低通滤波器:110,最平滑的滤波
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);// 滤波参数给最大
	// 陀螺仪配置寄存器:前三位,自测使能:全为0,不自测;满量程选择:11,最大量程;后三位无关位:为0
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);// 陀螺仪和加速度计都选最大量程
	// 加速度计配置寄存器:前三位,自测使能:全为0,不自测;满量程选择:11,最大量程;后三位高通滤波器:用不到,为000
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
		
}

// 获取芯片的ID号
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

// 获取寄存器数据的函数,返回6个int16_t的数据,分别表示XYZ的加速度值和陀螺仪值
// 指针地址传递的方法,返回多值
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
	                 int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	// 加速度计X
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL; // 高8位左移8位,再或上低8位,得到加速度计X轴的16位数据
	// 加速度计Y
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	// 加速度计Z
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	// 陀螺仪X
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	// 陀螺仪Y
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	// 陀螺仪Z
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyI2C.h"
#include "MPU6050.h"

uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;// 接收XYZ轴的加速度值和陀螺仪值



int main(void)
{
	OLED_Init();
//	MyI2C_Init();
	MPU6050_Init();
//	
	OLED_ShowString(1,1,"ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1, 4, ID, 2);
	
//	// 指定地址写
//	MyI2C_Start(); // 产生起始条件,开始一次传输
//	// 主机首先发送一个字节,内容是从机地址+读写位,进行寻址
//	MyI2C_SendByte(0xD0);  // 1101 000 0,0代表即将进行写入操作
//	// 发送一个字节后,要接收一下应答位,看看从机有没有收到刚才的数据
//	uint8_t Ack = MyI2C_ReceiveAck();
//	// 接收应答之后,要继续发送一个字节,写入寄存器地址
//	MyI2C_Stop();
//	
//	OLED_ShowNum(1, 1, Ack, 3);
	
//	// 指定地址读
//	uint8_t ID = MPU6050_ReadReg(0X75);// 返回值是0x68
//	OLED_ShowHexNum(1, 1, ID, 2);
	
//	// 指定地址写,需要先解除睡眠模式,否则写入无效
//	// 睡眠模式是电源管理寄存器1的这一位SLEEP控制的,把该寄存器写入0x00,解除睡眠模式
//	// 该寄存器地址是0x6B
//	MPU6050_WriteReg(0x6B, 0x00);
//	// 采样率分频寄存器,地址是0x19,值的内容是采样分频
//	MPU6050_WriteReg(0x19, 0xAA);
//	
//	uint8_t ID = MPU6050_ReadReg(0X19);
//	OLED_ShowHexNum(1, 1, ID, 2);//显示0x19地址下的内容,应该是0xAA
	
	while(1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
		OLED_ShowSignedNum(2, 1, AX, 5);
		OLED_ShowSignedNum(3, 1, AY, 5);
		OLED_ShowSignedNum(4, 1, AZ, 5);
		OLED_ShowSignedNum(2, 8, GX, 5);
		OLED_ShowSignedNum(3, 8, GY, 5);
		OLED_ShowSignedNum(4, 8, GZ, 5);
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

4. I2C peripherales

4.1 Introductio ad I2C peripherales

  • STM32 ferramentum I2C ambitus transceptivus integrat, quae sponte fungi potest functiones sicut generatio horologii, generationis conditio et initium et finis, responsum frenum transmissio et receptio, et notitia transmissio et receptio per ferramenta, reducendo onus in CPU.
  • Suscipe multi-hospes exemplum
  • Support VII-bit / X-bit oratio modus
  • Diversas velocitates communicationis sustinet, celeritas vexillum (usque ad 100 kHz), festinanter (usque ad 400 kHz)
  • Support DMA
  • Compatible cum SMBus protocol
  • STM32F103C8T6 hardware I2C opes: I2C1, I2C2

4.2 I2C obstructionum diagram

image.png

  • Sinistrae sunt fibulae communicationis: SDA, SCL;
    Fibulae ex generalibus periphericis cum externo per multiplicem modum GPIO portus coniungi solent.
  • Praecipua pars imperii notitiae est: SDA. Pars mediae transmissionis et receptionis datae actis mandare DR (DATA REGISTER) et mandare notitiae trabeae. Cum notitia mittenda eget, unus byte notitiarum ad actis mandare DR conscribi potest. In processu mobili, altera notitia directe in registro notitiae collocari potest et exspectatur. Postquam prior notitia transpositio completa est, altera notitia inconsutilis connexa et continua mitti potest. Cum e notitia registro ad trabea transpositio transfertur, TXE frenum status registri ad 1 apponitur, indicans tabulam transmittere vacuam esse.
  • Acceptio: Input data e paxillo ad trabea transpositio per partes movetur. Cum unus byte notitiarum colligitur, notitia ex tabula transmigratione ad actis mandare totum transfertur, et vexillum RXNE in tabulario positum est. eodem tempore, indicans receptionem. Cum autem ad recipiendum et quando mittere, scribere debes frena debita in registro ad operandum.
  • Comparator et electronica electronica in servili modo adhibentur.
  • SCL: Horologium imperium adhibetur ad lineam SCL moderandam. Scribere frenum respondente in registro horologii dicionis, et ambitus debitam functionem faciet. Circuitus logicae moderari, scribens ad dicionem registri totius ambitus potest regere. Status laboris ambitus cognosci potest ex registro status legendo.
  • Cum multa bytes mittens et accipientes, DMA ad efficientiam meliorem adhiberi possunt.

4.3 I2C basic structure

image.png

  • SDA: Quia I2C summus ordo primus est, haec trabea transmigratio ad sinistram vices. Cum mittit, mo- bit princeps primus, deinde secundus in altum mandit. Horologium SCL semel mutatum est et 8 vicibus mutatum, et 8 bytes in SDA linea ab alta parte ad paulum ad humilem collocari possunt. Cum accepto, notitia a dextra per portum GPIO movetur, et tandem octo vicibus movetur, recipitur unus byte. Data output est output ad portum per portum GPIO. Data inputatio in registro trabeo per GPIO portum initus est.
  • Portus GPIO configurari debet multis apertis exhauriendi output modum; multiplicatio significat statum GPIO portui in periphericis chippis refrenari, et output apertum esse portum configurationis ab I2C protocollo postulatum. Etiam in modo output aperto-exhaurienti, portus GPIO initus esse potest.
  • SCL: Horologium moderatoris lineam horologii per GPIO moderatur.
    image.png

4.4 Hostiam mittit

image.png
Cum STM32 scribere ad certam electronicam vult, necesse est ut sequentia tabula transmissionis transmissionis persequatur.

  • 7-bit inscriptio: Unus byte post initium conditio dirigitur
  • Inscriptio 10-bit: Duo bytes post condicionem initii compellant. Prima byte est tabulae capitis, et contentum est 5 frenum vexillum 11110 + 2-bit inscriptio + 1 lege scribe frenum; Oratio pura 8-bit.
  • 7-pars processus: initium, oratio ancillae, responsio, notitia, responsio, data, responsio..
  1. Post initializationem, bus defaltis ad statum otiosum, et STM defaltam servi modum. .

image.png

  1. Eventus EV5 considerari potest ut frenum vexillum.

image.png

  1. Tunc electronica servilis byte mittere potes. Servus electronicus scribendus est ad actis mandare DR missis ad I2C bus, et tunc ferramenta statim responsionem accipient et iudicem.
  2. Cum inscriptione peracta, eventus EV6 occurret et frenum vexillum ADDR erit 1. Hoc frenum vexillum indicat finem electronicae tradendae in modum domini.

image.png

  1. Eventus EV8_1 significat vexillum TxE esse 1, tabula trabea vacua est, et actis mandare inanis est. Non necesse est scribere actis mandare DR ut data mittas statim transitum transferre mandare mitto. Eventus EV8 occurret. Tabularium trabeum vacuum non est et actis mandare vacuum est, quod significat mutationem registri datam mittit. Hoc momento, notitia 2 in tabulario data scribetur et exspectat. Recepta responsionis frenum, notitia frena transferuntur in tabulam transmissionis transmissionis registri notitia vacua est, ut hoc tempore, quo EV8 res acta est.
  2. Tunc notitia 2 mittitur, sed tunc temporis notitia altera ad actis mandare scripta est et exspectat. Eventus EV8 detecto, altera notitia scribi potest.
  3. Post datam quam vis mittere scripta est, nulla notitia nova scripta est ad actis mandare. EV8_2 eventum, TxE=1 significat tabulam trabeam vacuam, actis mandare inanis est, BTF: byte transmissio vexilli finis, cum transmissio, cum nova notitia mittetur et actis mandare notata novis data non sunt scripta. Cum EV8_2 deprehenditur, conditio terminationis Desine generari potest. Terminationem conditionem generare, ut patet, frena debent esse in potestate registri qui coerceri potest. Hac ratione missio sequentia dicta est.

4.5 Host receptionis

image.png
7-bit dominus receptionis: initium, servus electronica + lege, responsionem accipere, data accipere, responsum mittere... data accipere, non responsum, terminare.

  1. Primum, scribe initium aliquantulum de registro dicionis generandi condicionem initium, et deinde eventum exspecta EV5 (indicans condicionem missum esse initium).
  2. Post allocutus, recipitur responsio, et eventum EV6 generatur post finem (indicans inscriptionem peractam).
  3. Data 1 significat data esse input per subcinctus mandare.
  4. EV6_1 indicat notitias adhuc mutatas esse. Recepta responsionis, significat mutationem registri feliciter in uno byte notitiarum moveri. 1. Hoc tempore, in byte mutatum transfertur in actis mandandi secundum totum, et. Vexillum RxNE eodem tempore positum est, indicans Tabularium notitia vacuum non esse, id est, unum byte notitiarum receptum esse. Status est eventus EV7, RxNE=1 Quod notitia recepta est.
  5. Scilicet, cum notitia 1 non recitata est, notitia 2 directa moveri potest in registro transpositio. Postea, transpositio notitiarum 2 perficitur, notitia 2 recipitur, eventum EV7 generatur, notitia 2 legitur, et. in EV7 eventu abiit.
  6. Cum amplius receptio non opus est, responsionis mandare imperium ACK debet ut 0 in antecessum apponatur, cum unitas leo ultima occurrit, et condicio terminationis postulatio ponitur, id est eventus EV7_1 dabitur.

4.6 Software / hardware waveform collatio

image.png

image.png

5. 10-2 Hardware I2C legere et scribere MPU6050

5.1 I2C munera bibliothecae


void I2C_DeInit(I2C_TypeDef* I2Cx);
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 生成起始条件、终止条件
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 配置CR1的ACK这一位,0:无应答,1:应答
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

// 发送数据,把Data数据直接写入到DR寄存器
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
// 读取DR,接收数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

// Address参数也是通过DR发送的,但在发送之前,设置了Address最低位的读写位,
// I2C_Direction不是发送,是把Address的最低位置1(读),否则最低位清0(写)
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.2 Hardware I2C legere et scribere MPU6050 exsecutionem

5.2.1 Hardware iunctio

SCL cum PB10 clavo STM32 coniungitur, et SDA cum PB11 paxillo coniungitur. Cum planum programmatum flipping hic perficiatur, duo portus GPIO pro voluntate coniungi possunt.
Summa notitia OLED est ID numerus machinae. Infra, tres a sinistris sunt outputa data accelerationis sensoris, quae sunt acceleratio axis X-axis, Y-axis et Z-axis respective. quae sunt velocitas angularis axis X-axis Y-axis & Z-axis.

5.2.2 Proventus Operatio

IMG_20240406_172128.jpg

5.2.3 Code implementation process

  1. Configurare I2C peripherales, initialize I2C peripherales, repone MyI2C_Init
    (1) Turn horologium periphericum I2C et portus GPIO respondentis;
    (2) Initialize portum GPIO ad periphericum I2C multiplicis cum modo patenti-fluctibus respondentem
    (3) Utere structura ad totam configurandam I2C
    (4) I2C_Cmd, da I2C*
  2. Circulos peripherales compesce, tempus scribendi animadverte in inscriptionibus specificatis, ac scribeReg
  3. Imperium periphericum circuii ut cognoscat leo legendi certam inscriptionem ac reponere ReadReg

5.2.4 Code

  1. Code MPU6050:
#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

/*
1. 配置I2C外设,对I2C外设进行初始化,替换MyI2C_Init
   (1)开启I2C外设和对应GPIO口的时钟,
   (2)把I2C外设对应的GPIO口初始化为复用开漏模式
   (3)使用结构体,对整个I2C进行配置
   (4)I2C_Cmd,使能I2C
2. 控制外设电路,实现指定地址写的时序,替换WriteReg
3. 控制外设电路,实现指定地址读的时序,替换ReadReg
*/


// 宏定义:从机地址
#define MPU6050_ADDRESS  0xD0
 
// 超时退出
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t TimeOut;
	TimeOut = 10000;
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) 
	{   
		TimeOut --;
		if (TimeOut == 0)
		{
			break;// 跳出循环,直接执行后面的程序
		}
	}
}

// 指定地址写:发送器传送时序
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址后,接收应答
//	MyI2C_ReceiveAck();// 寻址找到从机之后,继续发送下一个字节
//	MyI2C_SendByte(RegAddress); // 指定寄存器地址,存在MPU6050的当前地址指针里,用于指定具体读写哪个寄存器
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(Data);// 指定写入指定寄存器地址下的数据
//	MyI2C_ReceiveAck();
//	MyI2C_Stop();
	
	I2C_GenerateSTART(I2C2, ENABLE); // 起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件
	
	// 发送从机地址,接收应答。该函数自带了接收应答,如果应答错误,硬件会通过标志位和中断来提示我们
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6事件
	
	// 直接写入DR,发送数据
	I2C_SendData(I2C2, RegAddress);
	// 写入了DR,DR立刻转移到移位寄存器进行发送,EV8事件出现的非常快,基本不用等。因为有两级缓存,
	// 第一个数据写进DR了,会立刻跑到移位寄存器,这时不用等第一个数据发完,第二个数据就可以写进去等着了。
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8事件
	
	I2C_SendData(I2C2, Data);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2事件
	
	I2C_GenerateSTOP(I2C2, ENABLE);
}

// 指定地址读:接收器传送序列
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS);
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(RegAddress); // 指定地址:就是设置了MPU6050的当前地址指针
//	MyI2C_ReceiveAck();
//	// 转入读的时序,重新指定读写位,就必须重新起始
//	MyI2C_Start();// 重复起始条件
//	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);// 指定从机地址和读写位,0xD0是写地址,或上0x01变为0xD1,读写位为1,接下来要读从机的数据
//	MyI2C_ReceiveAck(); // 接收应答后,总线控制权就正式交给从机了,从机开始发送一个字节
//	Data = MyI2C_ReceiveByte();// 主机接收一个字节,该函数返回值就是接收到的数据
//	// 主机接收一个字节后,要给发送从机一个应答
//	MyI2C_SendAck(1);// 参数为0,就是给从机应答,参数给1,就是不给从机应答
//	MyI2C_Stop();
	
	
	I2C_GenerateSTART(I2C2, ENABLE); // 起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件
	
	// 发送从机地址,接收应答。该函数自带了接收应答,如果应答错误,硬件会通过标志位和中断来提示我们
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6事件
	
	// 直接写入DR,发送数据
	I2C_SendData(I2C2, RegAddress);
	// 写入了DR,DR立刻转移到移位寄存器进行发送,EV8事件出现的非常快,基本不用等。因为有两级缓存,
	// 第一个数据写进DR了,会立刻跑到移位寄存器,这时不用等第一个数据发完,第二个数据就可以写进去等着了。
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2事件
	
	I2C_GenerateSTART(I2C2, ENABLE);// 重复起始条件
	
	// 主机接收
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件
	// 接收地址
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); // 函数内部就自动将该地址MPU6050_ADDRESS的最低位置1
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //EV6事件
	
	// 在最后一个数据之前就要把应答位ACK置0,同时把停止条件生成位STOP置1
	I2C_AcknowledgeConfig(I2C2, DISABLE);
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //EV7事件
	// 等EV7事件产生后,一个字节的数据就已经在DR里面了。
	// 读取DR就可拿出该字节
	Data = I2C_ReceiveData(I2C2); // 返回值就是DR的数据
	// 在接收函数的最后,要恢复默认的ACK = 1。
	// 默认状态下ACK就是1,给从机应答,在收最后一个字节之前,临时把ACK置0,给非应答,
	// 所以在接收函数的最后,要恢复默认的ACK = 1,这个流程是为了方便指定地址收多个字节。
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	return Data;
}

void MPU6050_Init(void)
{
	
//	MyI2C_Init();
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 复用开漏
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; // 模式
	I2C_InitStructure.I2C_ClockSpeed = 50000; // 时钟速度,最大400kHz的时钟频率
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	// 时钟占空比,只有在时钟频率大于100kHz,也就是进入到快速状态时才有用,小于100kHz,占空比是固定的1:1,
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // STM32作为从机,可以响应几位的地址
	I2C_InitStructure.I2C_OwnAddress1 = 0x00; // 自身地址1,也是作为从机使用,
	I2C_Init(I2C2, &I2C_InitStructure); 
	
	I2C_Cmd(I2C2,ENABLE);
	
	// 写入一些寄存器对MPU6050硬件电路进行初始化配置
	// 电源管理寄存器1:设备复位:0,不复位;睡眠模式:0,解除睡眠:循环模式:0,不循环;无关位i:0;温度传感器失能:0,不失能;最后三位选择时钟:000,选择内部时钟,001,选择x轴的陀螺仪时钟,
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);// 解除睡眠,选择陀螺仪时钟
	// 电源管理寄存器2:前两位,循环模式唤醒频率:00,不需要;后6位,每一个轴的待机位:全为0,不需要待机;
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00); // 均不待机
	// 采样率分频:该8位决定了数据输出的快慢,值越小越快
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);// 采样分频:10分频
	// 配置寄存器:外部同步:全为0,不需要;数字低通滤波器:110,最平滑的滤波
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);// 滤波参数给最大
	// 陀螺仪配置寄存器:前三位,自测使能:全为0,不自测;满量程选择:11,最大量程;后三位无关位:为0
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);// 陀螺仪和加速度计都选最大量程
	// 加速度计配置寄存器:前三位,自测使能:全为0,不自测;满量程选择:11,最大量程;后三位高通滤波器:用不到,为000
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
		
}

// 获取芯片的ID号
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

// 获取寄存器数据的函数,返回6个int16_t的数据,分别表示XYZ的加速度值和陀螺仪值
// 指针地址传递的方法,返回多值
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
	                 int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	// 加速度计X
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL; // 高8位左移8位,再或上低8位,得到加速度计X轴的16位数据
	// 加速度计Y
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	// 加速度计Z
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	// 陀螺仪X
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	// 陀螺仪Y
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	// 陀螺仪Z
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  1. main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;// 接收XYZ轴的加速度值和陀螺仪值



int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	OLED_ShowString(1,1,"ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1, 4, ID, 2);
	
//	// 指定地址写
//	MyI2C_Start(); // 产生起始条件,开始一次传输
//	// 主机首先发送一个字节,内容时从机地址+读写位,进行寻址
//	MyI2C_SendByte(0xD0);  // 1101 000 0,0代表即将进行写入操作
//	// 发送一个字节后,要接收一下应答位,看看从机有没有收到刚才的数据
//	uint8_t Ack = MyI2C_ReceiveAck();
//	// 接收应答之后,要继续发送一个字节,写入寄存器地址
//	MyI2C_Stop();
//	
//	OLED_ShowNum(1, 1, Ack, 3);
	
//	// 指定地址读
//	uint8_t ID = MPU6050_ReadReg(0X75);// 返回值是0x68
//	OLED_ShowHexNum(1, 1, ID, 2);
	
//	// 指定地址写,需要先解除睡眠模式,否则写入无效
//	// 睡眠模式是电源管理寄存器1的这一位SLEEP控制的,把该寄存器写入0x00,解除睡眠模式
//	// 该寄存器地址是0x6B
//	MPU6050_WriteReg(0x6B, 0x00);
//	// 采样率分频寄存器,地址是0x19,值的内容是采样分频
//	MPU6050_WriteReg(0x19, 0xAA);
//	
//	uint8_t ID = MPU6050_ReadReg(0X19);
//	OLED_ShowHexNum(1, 1, ID, 2);//显示0x19地址下的内容,应该是0xAA
	
	
	
	while(1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
		OLED_ShowSignedNum(2, 1, AX, 5);
		OLED_ShowSignedNum(3, 1, AY, 5);
		OLED_ShowSignedNum(4, 1, AZ, 5);
		OLED_ShowSignedNum(2, 8, GX, 5);
		OLED_ShowSignedNum(3, 8, GY, 5);
		OLED_ShowSignedNum(4, 8, GZ, 5);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57